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 > BG wave effect (Mode 0)

#17896 - RaBBi - Tue Mar 16, 2004 9:09 pm

Hi all,

I've searched yet on the board but didn't find anything but a snippet from headspin about this subject.

this subject is about wave effect with BGs in tile mode (0 in my case).

I use HBlank interrupt and a sinetable (generated by SinusLab), and VBlank interrupt too.

The wave is good from the start but then, after some "turns", the bottom of the screen is fixed, and a group of scanlines seems to move up then disappear (it's hard to describe a visual effect...)
And that, in loop.

So can someone have an explanation about modus operandi for this, or perhaps a code snippet to show.

Thanks ^^
_________________
Sorry for my poor english, but it would be worst for you to understand me if I speak in my native language, French ^^

#17901 - dagamer34 - Tue Mar 16, 2004 10:35 pm

A picture is worth 1000 words...
_________________
Little kids and Playstation 2's don't mix. :(

#17902 - RaBBi - Tue Mar 16, 2004 10:41 pm

oki sorry, I'll post two pictures and a link to the *bad* result.

http://yumeteam.free.fr/gbadev/CoS.gba

EDIT: here the pics :

http://yumeteam.free.fr/gbadev/CoS_wave_ok.png

http://yumeteam.free.fr/gbadev/CoS_parasite.png
_________________
Sorry for my poor english, but it would be worst for you to understand me if I speak in my native language, French ^^

#17907 - Miked0801 - Wed Mar 17, 2004 2:03 am

2 ways:
1. Setup and HDMA every scanline that's dest is the BG scroll reg and source is a table of scroll offsets that are created and updated during vblank. The table is a magnitude * sin (theta) per scanline to give you the wave.

2. Every Scanline, interrupts and do the sine calc yourself depending on the LY. Slower, but easier to do at first.

#17910 - RaBBi - Wed Mar 17, 2004 2:34 am

Thanks Miked'

I did the second way apparently.

I use a sinus_counter variable that increment every vblank and I use it to calculate my index in the sintable (I use LCD_Y too).

But it seems that the problem comes after the 100th vblank...don't know why this particular value.

here my code :

Code:

struct BGPOINT
{
  u16 x, y;
};

#define BGSCROLL ((volatile struct BGPOINT *)0x04000010)

const s8 g_SinTab1[128*2]=
{ 2 , 3 , 5 , 6 , 8 , 9 , 11 , 12 ,
 14 , 15 , 16 , 18 , 19 , 20 , 21 , 23 ,
 24 , 25 , 26 , 27 , 27 , 28 , 29 , 30 ,
 30 , 31 , 31 , 31 , 32 , 32 , 32 , 32 ,
 32 , 32 , 32 , 31 , 31 , 31 , 30 , 30 ,
 29 , 28 , 27 , 27 , 26 , 25 , 24 , 23 ,
 21 , 20 , 19 , 18 , 16 , 15 , 14 , 12 ,
 11 , 9 , 8 , 6 , 5 , 3 , 2 , 0 ,
-2 ,-3 ,-5 ,-6 ,-8 ,-9 ,-11 ,-12 ,
-14 ,-15 ,-16 ,-18 ,-19 ,-20 ,-21 ,-23 ,
-24 ,-25 ,-26 ,-27 ,-27 ,-28 ,-29 ,-30 ,
-30 ,-31 ,-31 ,-31 ,-32 ,-32 ,-32 ,-32 ,
-32 ,-32 ,-32 ,-31 ,-31 ,-31 ,-30 ,-30 ,
-29 ,-28 ,-27 ,-27 ,-26 ,-25 ,-24 ,-23 ,
-21 ,-20 ,-19 ,-18 ,-16 ,-15 ,-14 ,-12 ,
-11 ,-9 ,-8 ,-6 ,-5 ,-3 ,-2 , 0,
 2 , 3 , 5 , 6 , 8 , 9 , 11 , 12 ,
 14 , 15 , 16 , 18 , 19 , 20 , 21 , 23 ,
 24 , 25 , 26 , 27 , 27 , 28 , 29 , 30 ,
 30 , 31 , 31 , 31 , 32 , 32 , 32 , 32 ,
 32 , 32 , 32 , 31 , 31 , 31 , 30 , 30 ,
 29 , 28 , 27 , 27 , 26 , 25 , 24 , 23 ,
 21 , 20 , 19 , 18 , 16 , 15 , 14 , 12 ,
 11 , 9 , 8 , 6 , 5 , 3 , 2 , 0 ,
-2 ,-3 ,-5 ,-6 ,-8 ,-9 ,-11 ,-12 ,
-14 ,-15 ,-16 ,-18 ,-19 ,-20 ,-21 ,-23 ,
-24 ,-25 ,-26 ,-27 ,-27 ,-28 ,-29 ,-30 ,
-30 ,-31 ,-31 ,-31 ,-32 ,-32 ,-32 ,-32 ,
-32 ,-32 ,-32 ,-31 ,-31 ,-31 ,-30 ,-30 ,
-29 ,-28 ,-27 ,-27 ,-26 ,-25 ,-24 ,-23 ,
-21 ,-20 ,-19 ,-18 ,-16 ,-15 ,-14 ,-12 ,
-11 ,-9 ,-8 ,-6 ,-5 ,-3 ,-2 , 0 };


void hblfunc(void)
{
 
  //if(LCD_Y<160)
  //{
    BGSCROLL[1].x = (g_SinTab1[LCD_Y+sinus_count+(sinus_offset)]);
   
    BGSCROLL[2].x = (g_SinTab1[LCD_Y+sinus_count+(sinus_offset)]);
   
    //sinus_offset++;
  //}

}

void vblfunc(void)
{
   sinus_count++;
}


Interrupts are correctly handled in IWRAM.
And I use tepples'gba header.
_________________
Sorry for my poor english, but it would be worst for you to understand me if I speak in my native language, French ^^

#17911 - RaBBi - Wed Mar 17, 2004 3:21 am

I solved my problem using an array of 128*4 entries instead of 128*2.

I tried to figure out why but didn't find the reason for the moment.

Could someone explain me this?

Thanks ^^
_________________
Sorry for my poor english, but it would be worst for you to understand me if I speak in my native language, French ^^

#17912 - Miked0801 - Wed Mar 17, 2004 3:25 am

I'd do this


Code:

   u8 theta =  LCD_Y + sinus_count + sinus_offset;

   BGSCROLL[1].x = (g_SinTab1[theta]);
   BGSCROLL[2].x = (g_SinTab1[theta]);
 


This makes sure you don't overflow your table - which is my guess at what your problem is.

#17919 - RaBBi - Wed Mar 17, 2004 12:50 pm

Yeah you're right Miked' ^^

It was an overflow problem, cause sinus_count+LCD_Y was over > 256 (number of entries from the precedent sintable).

But, now that it works, I have a little question about your first way.

Why use HDMA for a small operation like put a value in a scroll register?

Thanks ^^
_________________
Sorry for my poor english, but it would be worst for you to understand me if I speak in my native language, French ^^

#17920 - headspin - Wed Mar 17, 2004 1:06 pm

hehe you found the same problem I was having in my code ;)

I posted what I had at the time, but I never did get around to fixing the sine table looping/overflow problem...

Perhaps you could post your code Rabbi or send it to me via e-mail.. I am going to add the sine wave effect to my tutorial one of these days. Would save me the hasstle of re-writing it and I'll credit your help.
_________________
Warhawk DS | Manic Miner: The Lost Levels | The Detective Game

#17924 - RaBBi - Wed Mar 17, 2004 4:46 pm

There's no problem headspin ^^

I'll be glad to contribute to your tutorial I enjoy reading ;)

I'll post my code below, but I must say that I'm not an experimented developer and it certainly needs to be optimised.

I didn't get deeper in this effect, that was just to have a vision about the global way to realize it.
If you can mix your approach and mine, it would be a nice demonstration I think ^^

Here's the code for main.c :

Code:

void vblfunc(void);
void hblfunc(void);

u16 scrollx = 0; // used to update BG scroll when using pad
u16 scrolly = 0; // same
u8 sinus_offset=0; // for next use
u8 sinus_count=0;
ISR saved_isr;

const s8 g_SinTab1[128*4]=
{ 2 , 3 , 5 , 6 , 8 , 9 , 11 , 12 ,
 14 , 15 , 16 , 18 , 19 , 20 , 21 , 23 ,
 24 , 25 , 26 , 27 , 27 , 28 , 29 , 30 ,
 30 , 31 , 31 , 31 , 32 , 32 , 32 , 32 ,
 32 , 32 , 32 , 31 , 31 , 31 , 30 , 30 ,
 29 , 28 , 27 , 27 , 26 , 25 , 24 , 23 ,
 21 , 20 , 19 , 18 , 16 , 15 , 14 , 12 ,
 11 , 9 , 8 , 6 , 5 , 3 , 2 , 0 ,
-2 ,-3 ,-5 ,-6 ,-8 ,-9 ,-11 ,-12 ,
-14 ,-15 ,-16 ,-18 ,-19 ,-20 ,-21 ,-23 ,
-24 ,-25 ,-26 ,-27 ,-27 ,-28 ,-29 ,-30 ,
-30 ,-31 ,-31 ,-31 ,-32 ,-32 ,-32 ,-32 ,
-32 ,-32 ,-32 ,-31 ,-31 ,-31 ,-30 ,-30 ,
-29 ,-28 ,-27 ,-27 ,-26 ,-25 ,-24 ,-23 ,
-21 ,-20 ,-19 ,-18 ,-16 ,-15 ,-14 ,-12 ,
-11 ,-9 ,-8 ,-6 ,-5 ,-3 ,-2 , 0,
2 , 3 , 5 , 6 , 8 , 9 , 11 , 12 ,
 14 , 15 , 16 , 18 , 19 , 20 , 21 , 23 ,
 24 , 25 , 26 , 27 , 27 , 28 , 29 , 30 ,
 30 , 31 , 31 , 31 , 32 , 32 , 32 , 32 ,
 32 , 32 , 32 , 31 , 31 , 31 , 30 , 30 ,
 29 , 28 , 27 , 27 , 26 , 25 , 24 , 23 ,
 21 , 20 , 19 , 18 , 16 , 15 , 14 , 12 ,
 11 , 9 , 8 , 6 , 5 , 3 , 2 , 0 ,
-2 ,-3 ,-5 ,-6 ,-8 ,-9 ,-11 ,-12 ,
-14 ,-15 ,-16 ,-18 ,-19 ,-20 ,-21 ,-23 ,
-24 ,-25 ,-26 ,-27 ,-27 ,-28 ,-29 ,-30 ,
-30 ,-31 ,-31 ,-31 ,-32 ,-32 ,-32 ,-32 ,
-32 ,-32 ,-32 ,-31 ,-31 ,-31 ,-30 ,-30 ,
-29 ,-28 ,-27 ,-27 ,-26 ,-25 ,-24 ,-23 ,
-21 ,-20 ,-19 ,-18 ,-16 ,-15 ,-14 ,-12 ,
-11 ,-9 ,-8 ,-6 ,-5 ,-3 ,-2 , 0,
2 , 3 , 5 , 6 , 8 , 9 , 11 , 12 ,
 14 , 15 , 16 , 18 , 19 , 20 , 21 , 23 ,
 24 , 25 , 26 , 27 , 27 , 28 , 29 , 30 ,
 30 , 31 , 31 , 31 , 32 , 32 , 32 , 32 ,
 32 , 32 , 32 , 31 , 31 , 31 , 30 , 30 ,
 29 , 28 , 27 , 27 , 26 , 25 , 24 , 23 ,
 21 , 20 , 19 , 18 , 16 , 15 , 14 , 12 ,
 11 , 9 , 8 , 6 , 5 , 3 , 2 , 0 ,
-2 ,-3 ,-5 ,-6 ,-8 ,-9 ,-11 ,-12 ,
-14 ,-15 ,-16 ,-18 ,-19 ,-20 ,-21 ,-23 ,
-24 ,-25 ,-26 ,-27 ,-27 ,-28 ,-29 ,-30 ,
-30 ,-31 ,-31 ,-31 ,-32 ,-32 ,-32 ,-32 ,
-32 ,-32 ,-32 ,-31 ,-31 ,-31 ,-30 ,-30 ,
-29 ,-28 ,-27 ,-27 ,-26 ,-25 ,-24 ,-23 ,
-21 ,-20 ,-19 ,-18 ,-16 ,-15 ,-14 ,-12 ,
-11 ,-9 ,-8 ,-6 ,-5 ,-3 ,-2 , 0,
2 , 3 , 5 , 6 , 8 , 9 , 11 , 12 ,
 14 , 15 , 16 , 18 , 19 , 20 , 21 , 23 ,
 24 , 25 , 26 , 27 , 27 , 28 , 29 , 30 ,
 30 , 31 , 31 , 31 , 32 , 32 , 32 , 32 ,
 32 , 32 , 32 , 31 , 31 , 31 , 30 , 30 ,
 29 , 28 , 27 , 27 , 26 , 25 , 24 , 23 ,
 21 , 20 , 19 , 18 , 16 , 15 , 14 , 12 ,
 11 , 9 , 8 , 6 , 5 , 3 , 2 , 0 ,
-2 ,-3 ,-5 ,-6 ,-8 ,-9 ,-11 ,-12 ,
-14 ,-15 ,-16 ,-18 ,-19 ,-20 ,-21 ,-23 ,
-24 ,-25 ,-26 ,-27 ,-27 ,-28 ,-29 ,-30 ,
-30 ,-31 ,-31 ,-31 ,-32 ,-32 ,-32 ,-32 ,
-32 ,-32 ,-32 ,-31 ,-31 ,-31 ,-30 ,-30 ,
-29 ,-28 ,-27 ,-27 ,-26 ,-25 ,-24 ,-23 ,
-21 ,-20 ,-19 ,-18 ,-16 ,-15 ,-14 ,-12 ,
-11 ,-9 ,-8 ,-6 ,-5 ,-3 ,-2 , 0 };


void AgbMain(void)
{

  /* Init interuption handler */
  INTENABLE = 0;
  saved_isr = GET_MASTER_ISR();
  SET_MASTER_ISR(isr);
  LCDSTAT |= LCDSTAT_VBLINT;
  INTMASK |= INT_VBLANK;
  LCDSTAT |= LCDSTAT_HBLINT;
  INTMASK |= INT_HBLANK;
  INTENABLE = 1;

  ...

  while(1)
  {
      // in the main loop, scrollx and scrolly are updated when pad is pressed
      // not relative to the wave effect
  }

}

void hblfunc(void)
{

    // I tested Y wave effect, it was really fun to see ! ^^
    // I commented these lines

    // sinus_offset is not used for the moment, always 0

    BGSCROLL[1].x = scrollx+(g_SinTab1[LCD_Y+sinus_count+(sinus_offset)]);
    //BGSCROLL[1].y = scrolly+(g_SinTab1[LCD_Y+sinus_count+(sinus_offset)]);

    BGSCROLL[2].x = scrollx+(g_SinTab1[LCD_Y+sinus_count+(sinus_offset)]);
    //BGSCROLL[2].y = scrolly+(g_SinTab1[LCD_Y+sinus_count+(sinus_offset)]);

}

void vblfunc(void)
{
   sinus_count++;

   // not relative to the wave effect
   // it's just for scrolling BG with pad
   BGSCROLL[1].x = scrollx;
   BGSCROLL[1].y = scrolly;
   BGSCROLL[2].x = scrollx;
   BGSCROLL[2].y = scrolly;
}




And here the code for the ISR (in isr.iwram.c) :

Code:

void isr(void)
{
  u16 ack;

  INTENABLE = 0;
  ack = INTACK;
  if (ack & INT_HBLANK)
  {
    hblfunc();
  }
  if (ack & INT_VBLANK)
  {
    vblfunc();
  }

  BIOS_INTACK = ack;
  INTACK = ack;

  INTENABLE = 1;
}



And that's all!
The GBA hardware is really fantastic ;)

NOTE: I used tepples'pineight's GBA header, so please refer to this for used defines and macros ;)

Hope it helps ^^
And I repeat : it's was just a test !
If someone want to enhance this, please let us know ;)
_________________
Sorry for my poor english, but it would be worst for you to understand me if I speak in my native language, French ^^

#17960 - Miked0801 - Wed Mar 17, 2004 11:07 pm

Quote:

But, now that it works, I have a little question about your first way.

Why use HDMA for a small operation like put a value in a scroll register?

Thanks ^^


Because of all the overhead required to get into your interrupt, do the processing, and exit back to the main game. You wouldn't be able to do something like this in real-time as you're talking roughly 80+% of your CPU time in interrupt management doing this per line. This is why Mode7 stuff is done with HDMAs (and that stuff doesn't right more than a few bytes per line either). This is why this should be done that way too. It removes almost all the overhead by having the system do the assigning for you. Doing with HDMAs will also fix any screen tearing/glitching I bet you're seeing right now as you aren't waiting for an HBlank period before writing your scroll register changes.

Mike

#17970 - poslundc - Thu Mar 18, 2004 12:32 am

Miked0801 wrote:
Because of all the overhead required to get into your interrupt, do the processing, and exit back to the main game. You wouldn't be able to do something like this in real-time as you're talking roughly 80+% of your CPU time in interrupt management doing this per line. This is why Mode7 stuff is done with HDMAs (and that stuff doesn't right more than a few bytes per line either). This is why this should be done that way too. It removes almost all the overhead by having the system do the assigning for you. Doing with HDMAs will also fix any screen tearing/glitching I bet you're seeing right now as you aren't waiting for an HBlank period before writing your scroll register changes.


Now, now, Mike, you know perfectly well that a small, well-optimized ISR is perfectly manageable in HBlank despite the overhead.

My Mode7 stuff is done through ISRs and there's no problem with it, and it enables me to combine the Mode7 with other raster effects (like gradient fog and sky, and switching from displaying horizon to terrain on the same BG without using a VCOUNT interrupt).

HDMA is great if you have a single raster effect you're creating. Otherwise, interrupts are the way to go. And yes, when it comes to doing stuff in HBlank you will probably want to write your own ISR (instead of using the one in DKA) and you will probably want to code it in assembly.

Dan.

#17975 - Miked0801 - Thu Mar 18, 2004 12:50 am

:)

Just trying to keep it easy(er). But of course you are correct.

#18009 - ScottLininger - Thu Mar 18, 2004 5:20 pm

Hey guys,

I'm just trying to follow this thread and I realize that I have no idea what ISR means... I figured out HDMA from the cowbite spec, but ISR eludes me. (Probably belongs in the beginners forum, but while you're talking about it anyway...)

Anyone feel like filling me in?

Thanks!

Scott

#18010 - poslundc - Thu Mar 18, 2004 5:32 pm

Interrupt Service Routine

(Can apply both to the general interrupt subroutine the GBA branches to whenever an interrupt occurs, or to a more specific subroutine designed to handle a specific type of interrupt.)

Dan.

#18672 - LOst? - Wed Mar 31, 2004 2:57 pm

This wave effect is one of the coolest effects you can do on a 2-d console. I wish it was possible to do it on the PC but it isn't.

I must really learn how to set up the HDMA so I can do these effects without loosing cycles like when calling ISR

How would the HDMA react if you have a HBlank interrupt running as well?

Is this the order of the interrupt calls:
VBlank scanline 0
HBlank scanline 1
HBlank scanline 2
HBlank scanline 3, etc?

How would the order of the HDMA be?

#18674 - poslundc - Wed Mar 31, 2004 3:17 pm

LOst? wrote:
This wave effect is one of the coolest effects you can do on a 2-d console. I wish it was possible to do it on the PC but it isn't.


...

Quote:
I must really learn how to set up the HDMA so I can do these effects without loosing cycles like when calling ISR


Have a look at any number of tutorials, the Cowbite spec, or search the forum. You should find everything you need.

Quote:
How would the HDMA react if you have a HBlank interrupt running as well?


HDMA should preempt the interrupt. I'm sure someone will correct me if I am wrong on this.

Quote:
Is this the order of the interrupt calls:
VBlank scanline 0
HBlank scanline 1
HBlank scanline 2
HBlank scanline 3, etc?


You don't have to use interrupts for VBlank. But in general for raster effects you follow that pattern insofar as you set up scanline 0 in VBlank, then when REG_VCOUNT is 0 you set up scanline 1 in an HBlank interrupt, then scanline 2 when REG_VCOUNT is 1 in the next HBlank interrupt, etc.

Quote:
How would the order of the HDMA be?


No different, you just activate the DMA during VBlank.

Dan.