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 > Help needed with evil global interrupt variable

#93832 - LOst? - Fri Jul 21, 2006 4:08 pm

This is for the Nintendo DS, but should probably be the same on GBA.

I have a global variable declared like this:
Code:

// h file:
namespace BLAH
{
 extern volatile s32 mouse_x;
}

// cpp file:
namespace BLAH
{
 volatile s32 mouse_x;
}


This variable is accessible through every cpp file that have included the h file by doing this:
Code:

#include "h file" // You get the idea ok!

{
 BLAH::mouse_x += 30;
}


This works. Until the evil Vblank interrut reads the variable for the first time. The evil interrupt routine is part of the BLAH namespace and is working! Looks like this:
Code:

namespace BLAH
{
 volatile SpriteEntry sprites [128];

 void Vblank ()
 {
  // Disable interupts
  REG_IME = 0;

  sprites [0].attribute [1] = ((sprites [0].attribute [1] & 0xFE00) | (mouse_x - 16) & 0x1FF);

  DC_FlushAll ();
  dmaCopy ((SpriteEntry*) sprites, OAM, 128 * sizeof (SpriteEntry));

  // Vblank is done
  REG_IF = REG_IF;
  VBLANK_INTR_WAIT_FLAGS = REG_IF | REG_IE;

  // Enable interupts
  REG_IME = 1; 
 }
}


I get the sprite up. It is where mouse_x was at. But accessing mouse_x again, trying to update it's contents will fail.

Now I have tried a lot of things for a long time (including removing the namespace, saving copies of the variable before it enters the Vblank and restore it after, trying EWRAM_BSS declarations). I only ask for help when I am out of ideas.

Thanks for any help!
_________________
Exceptions are fun

#93883 - LOst? - Fri Jul 21, 2006 8:22 pm

I have located the problem to the interupt. For some reason it won't return from Vblank. It hangs there.

EDIT:
It is the REG_IF = REG_IF that hangs the program. Hmm, I need to replace it with something but what?
_________________
Exceptions are fun

#93903 - MrD - Fri Jul 21, 2006 10:11 pm

LOst? wrote:
I have located the problem to the interupt. For some reason it won't return from Vblank. It hangs there.

EDIT:
It is the REG_IF = REG_IF that hangs the program. Hmm, I need to replace it with something but what?


It's possible that it might be optimised away... is your REG_IF define volatile?

Try REG_IF |= REG_IF.

You could also try the more specific interrupt acknowledgement:

REG_IF |= IRQ_VBLANK;
_________________
Not active on this forum. For Lemmings DS help see its website.

#93960 - tepples - Sat Jul 22, 2006 2:40 am

Should never do REG_IF |= anything. The REG_IF register already has |= semantics coded into its plain =.

The ISR should look like this:
Code:
void isr(void) {
  int interrupts = REG_IF;

  if (interrupts & INT_SERIAL) {
    /* ... */
  }
  if (interrupts & INT_HBLANK) {
    /* ... */
  }
  if (interrupts & INT_VBLANK) {
    /* ... */
  }
  INTRWAIT_FLAGS |= interrupts;  // operator |= because it's plain memory
  REG_IF = interrupts; // operator = because it's a register with side effects
}


EDIT: express the intent more clearly
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.


Last edited by tepples on Sat Jul 22, 2006 4:07 am; edited 1 time in total

#93967 - wintermute - Sat Jul 22, 2006 3:37 am

tepples wrote:
Should never do REG_IF |= anything. The REG_IF register already has |= semantics coded into its plain =.

The ISR should look like this:
Code:
void isr(void) {
  int interrupts = REG_IF;
  if (interrupts & INT_VBLANK) {
    /* ... */
  }
  INTRWAIT_FLAGS |= interrupts;  // operator |= because it's plain memory
  REG_IF = interrupts; // operator = because it's a register with side effects
}


That's completely and utterly wrong.

The reason you shouldn't use |= on REG_IF is because setting bits in that register clears the interrupt associated with that bit. Using |= reads the register, ors in the new bits then writes the whole thing back, thus clearing *all* interrupts which are pending. The code you've written here does exactly the same thing in a slightly different way. You should only write a single bit to REG_IF, the bit associated with the interrupt you've just serviced. You do this because another interrupt may become pending while you are servicing another interrupt.
_________________
devkitPro - professional toolchains at amateur prices
devkitPro IRC support
Personal Blog

#93968 - tepples - Sat Jul 22, 2006 3:54 am

wintermute wrote:
That's completely and utterly wrong.

The last half-dozen times that people have said that to me on other boards, we ended up in "violent agreement".

Quote:
The reason you shouldn't use |= on REG_IF is because setting bits in that register clears the interrupt associated with that bit.

That's what I meant by "built-in |= semantics".

Quote:
Using |= reads the register, ors in the new bits then writes the whole thing back, thus clearing *all* interrupts which are pending. The code you've written here does exactly the same thing in a slightly different way. You should only write a single bit to REG_IF, the bit associated with the interrupt you've just serviced.

In my code, the variable 'interrupts' has true values for only those interrupts that the ISR just serviced.

Quote:
You do this because another interrupt may become pending while you are servicing another interrupt.

Another interrupt from a different source (different bit in REG_IF), or another interrupt from the same source (same bit in REG_IF)?
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#93970 - DekuTree64 - Sat Jul 22, 2006 4:01 am

There's not enough info to make the call of wether it's good or bad. If you did like
Code:
  if (interrupts & INT_VBLANK) {
    /* ... */
  }

  if (interrupts & INT_HBLANK) {
    /* ... */
  }

Then it would be fine. But if you did else if HBlank, it would be bad. Personally I prefer the else if method, writing only the bit for the one I processed to IF. Multiple interrupts triggering at the exact same time is rare, so it's faster to let the handler run twice than to check for all of them every time.
_________________
___________
The best optimization is to do nothing at all.
Therefore a fully optimized program doesn't exist.
-Deku

#93972 - tepples - Sat Jul 22, 2006 4:06 am

DekuTree64 wrote:
If you did like
Code:
  if (interrupts & INT_VBLANK) {
    /* ... */
  }

  if (interrupts & INT_HBLANK) {
    /* ... */
  }

Then it would be fine.

And this in fact is what my ISRs do. I'll edit my earlier example to make this more specific.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#93976 - wintermute - Sat Jul 22, 2006 4:20 am

tepples wrote:


Quote:
The reason you shouldn't use |= on REG_IF is because setting bits in that register clears the interrupt associated with that bit.

That's what I meant by "built-in |= semantics".


built in ^= semantics maybe. Writing a 1 clears the bit.


Quote:

In my code, the variable 'interrupts' has true values for only those interrupts that the ISR just serviced.


The variable has true bits for the interrupts which required servicing at the start of the dispatcher code. It doesn't take account of which interrupts are enabled. You're right though, it won't clear interrupts which haven't yet been serviced. Not as bad as I thought it was at first glance.

Quote:
Quote:
You do this because another interrupt may become pending while you are servicing another interrupt.

Another interrupt from a different source (different bit in REG_IF), or another interrupt from the same source (same bit in REG_IF)?


Either, depending on how long your interrupt takes.
_________________
devkitPro - professional toolchains at amateur prices
devkitPro IRC support
Personal Blog

#93978 - MrD - Sat Jul 22, 2006 4:28 am

MrD wrote:
REG_IF |= IRQ_VBLANK;

I wonder... where the hell did I read that?
_________________
Not active on this forum. For Lemmings DS help see its website.

#93987 - LOst? - Sat Jul 22, 2006 9:11 am

Well, I can see a lot of you have different techniques to handle an interrupt or more interrupts at the same time. And I believe it works for all of you because you have never tested all the exceptions that can happen.

I will design my program after what's currently working. That means I will go with one interrupt only, and try to build everything around it. I once hoped for more but it isn't time for it yet.

The |= is wierd because it was just & (anded) in the if statement above meaning the bit is already on. Wierd. I have a choice to use it of not, and I am using it..... because most people do use that in their code :P

I am happy to inform you that my code is working on my real DS after a week of programming only in an emulator. I never saw it actually hang and I thought it run much faster that way (more fps). Now it runs okay, or slow depending on what programming technique I am going for.

Thanks for all replies!
_________________
Exceptions are fun