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.

C/C++ > Interrupts not working on real hardware

#46806 - paladine - Fri Jul 01, 2005 12:42 am

My interrupts are working just fine and dandy in VBA but when I load my image to the real hardware it never gets called at all! I'm using a plain makefile setup with Jeff's crtls, crt0.s 1.28 and lnkscript 1.3.
Here's some sample code
Code:

CODE_IN_IWRAM void InterruptProcess()
{
  u16 intFlag = REG_IF;
  /* turn off interrupts */
  REG_IME = 0;
 
  static int i=0;
  if ((i++) & 1)
  {
    static u16 full = 0x7FFF;
    DMACopy(&full,VIDEO_BUFFER,38400,DMA_16NOW | DMA_SOURCE_FIXED);
  }
  else
  {
    static u16 empty = 0;
    DMACopy(&empty,VIDEO_BUFFER,38400,DMA_16NOW | DMA_SOURCE_FIXED);
  }
 
  /* service interrupt */
  if ((intFlag & INT_VBLANK))
  {
    /* perform the DMA transfers */
    /*while (!list_empty(&dmaList))
    {
      LPDMATRANSFER pDMA = (LPDMATRANSFER)list_pop(&dmaList);
      // perform transfer
      REG_DMA0SAD = (u32)(pDMA->src);
      REG_DMA0DAD = (u32)(pDMA->dest);
      REG_DMA0CNT = pDMA->flags;
      // remove from list
      list_del(&pDMA->list);
    } */
    /* update sprites */
    SpriteCopyToOAM();
  }
  REG_IF |= intFlag;
  SWI_ACKNOWLEDGE |= intFlag;
  /* turn them back on */
  REG_IME = 1;
}


and the setup code

Code:

/* no interrupts quite yet */
  REG_IME = 0;

  REG_DISPCNT = (3 | BG2_ENABLE | OBJ_ENABLE | OBJ_MAP_1D);
  /* turn on vblank interrupts */
  REG_IE |= INT_VBLANK | INT_HBLANK;
  REG_DISPSTAT |= REG_DISPSTAT_ENABLE_VBLANK | REG_DISPSTAT_ENABLE_HBLANK;
  /* turn on timers */
  REG_TM0CNT = TIMER_FREQUENCY_1024 | TIMER_ENABLE;
  REG_TM1CNT = TIMER_OVERFLOW | TIMER_ENABLE;
  /* turn off backgrounds */
  REG_BG0CNT = 0;
  REG_BG1CNT = 0;
  REG_BG2CNT = 0;
  REG_BG3CNT = 0;
  // turn off blending
  REG_BLDMOD = 0;
  /* turn interrupts on */
  REG_IME = 1;


Judging from the code it should make the screen quickly go from white to black. But no interrupts fire so nothing happens, just stays frozen.

#46855 - FluBBa - Fri Jul 01, 2005 9:40 am

Judging from the code you're trying to fill the entire screen every HBLANK is that what you want?
_________________
I probably suck, my not is a programmer.

#46857 - NoMis - Fri Jul 01, 2005 10:26 am

You have to make sure your interrupt handler is compiled as ARM.

NoMis
_________________
www.gamedev.at - The austrian gamedev site
hde.gamedev.at - The Handheld Dev Env plugins for Eclipse

#46866 - paladine - Fri Jul 01, 2005 2:26 pm

Yes I'm aware I'm changing the entire screen on every hblank. The purpose is to prove that I'm getting interrupts on the real hardware - which I believe I am not. If the screen goes whacky wild, then I know I am succeeding and can code what I really want ;-)

And yes, the code is compiled as arm. Here is the output from gcc
Code:

  1                    .file "interrupts.c"
   2                    .global dmaList
   3                    .data
   4                    .align 2
   5                    .type dmaList,%object
   6                    .size dmaList,8
   7                    dmaList:
   8 0000 00000000      .word dmaList
   9 0004 00000000      .word dmaList
  10                    .section .iwram,"ax",%progbits
  11                    .align 2
  12                    .global InterruptProcess
  13                    .type InterruptProcess,%function
  14                    InterruptProcess:
  15
  16
  17
  18 0000 0DC0A0E1      mov ip,sp
  19 0004 70D82DE9      stmfd sp!,{r4,r5,r6,fp,ip,lr,pc}
  20 0008 023CA0E3      mov r3,#512
  21 000c 813383E2      add r3,r3,#67108866
  22 0010 B020D3E1      ldrh r2,[r3,#0]
  23 0014 0133A0E3      mov r3,#67108864
  24 0018 0260A0E1      mov r6,r2
  25 001c 823F83E2      add r3,r3,#520
  26 0020 010012E3      tst r2,#1
  27 0024 0020A0E3      mov r2,#0
  28 0028 04B04CE2      sub fp,ip,#-4294967292
  29 002c B020C3E1      strh r2,[r3,#0]
  30 0030 2500000A      beq .L2
  31 0034 A0409FE5      ldr r4,.L15
  32 0038 003094E5      ldr r3,[r4,#0]
  33 003c 040053E1      cmp r3,r4
  34 0040 2200000A      beq .L13
  35 0044 0450A0E1      mov r5,r4
  36 0048 01E3A0E3      mov lr,#67108864
  37                    .L11:
  38 004c 003094E5      ldr r3,[r4,#0]
  39 0050 041093E5      ldr r1,[r3,#4]
  40 0054 002093E5      ldr r2,[r3,#0]
  41 0058 002081E5      str r2,[r1,#0]
  42 005c 041082E5      str r1,[r2,#4]
  43 0060 00C093E5      ldr ip,[r3,#0]
  44 0064 050093E9      ldmib r3,{r0,r2}
  45 0068 0C1093E5      ldr r1,[r3,#12]
  46 006c 00C080E5      str ip,[r0,#0]
  47 0070 B0208EE5      str r2,[lr,#176]
  48 0074 B4108EE5      str r1,[lr,#180]
  49 0078 002095E5      ldr r2,[r5,#0]
  50 007c 101093E5      ldr r1,[r3,#16]
  51 0080 050052E1      cmp r2,r5
  52 0084 B8108EE5      str r1,[lr,#184]
  53 0088 04008CE5      str r0,[ip,#4]
  54 008c 1100001A      bne .L11
  55                    .L13:
  56 0090 48309FE5      ldr r3,.L15+4
  57 0094 0FE0A0E1      mov lr,pc
^LARM GAS                       page 2


  58 0098 13FF2FE1      bx r3
  59                    .L2:
  60 009c 022CA0E3      mov r2,#512
  61 00a0 812382E2      add r2,r2,#67108866
  62 00a4 B030D2E1      ldrh r3,[r2,#0]
  63 00a8 033086E1      orr r3,r6,r3
  64 00ac B030C2E1      strh r3,[r2,#0]
  65 00b0 0314A0E3      mov r1,#50331648
  66 00b4 7F1C81E2      add r1,r1,#32512
  67 00b8 B83FD1E1      ldrh r3,[r1,#248]
  68 00bc 0123A0E3      mov r2,#67108864
  69 00c0 033086E1      orr r3,r6,r3
  70 00c4 B83FC1E1      strh r3,[r1,#248]
  71 00c8 822F82E2      add r2,r2,#520
  72 00cc 0130A0E3      mov r3,#1
  73 00d0 B030C2E1      strh r3,[r2,#0]
  74 00d4 70689DE8      ldmfd sp,{r4,r5,r6,fp,sp,lr}
  75 00d8 1EFF2FE1      bx lr

#46867 - FluBBa - Fri Jul 01, 2005 2:31 pm

DMA stops the cpu while it is running, filling the entire screen takes a lot of time, well at least a lot more then one raster row so you will end up with very unpredictable code in this case.
_________________
I probably suck, my not is a programmer.

#46869 - paladine - Fri Jul 01, 2005 2:45 pm

Yes that is true, the DMA probably takes longer than the entire HBLANK cycle. I took your advice, disasble HBLANK processing and wrote code to update the first 8 pixels only so as not to abuse the CPU. Still nothing on the real hardware :(

Code:

CODE_IN_IWRAM void InterruptProcess()
{
  u16 intFlag = REG_IF;
  /* turn off interrupts */
  REG_IME = 0;
 
  static int i=0;
  int j;
  if ((i++) & 1)
  {
    static const u16 full = 0x7FFF;
    for (j=0;j<8;++j)
      VIDEO_BUFFER[j] = full;
  }
  else
  {
    static u16 empty = 0;
    for (j=0;j<8;++j)
      VIDEO_BUFFER[j] = empty;
  }
 
  /* service interrupt */
  if ((intFlag & INT_VBLANK))
  {
    /* perform the DMA transfers */
    while (!list_empty(&dmaList))
    {
      LPDMATRANSFER pDMA = (LPDMATRANSFER)list_pop(&dmaList);
      // perform transfer
      REG_DMA0SAD = (u32)(pDMA->src);
      REG_DMA0DAD = (u32)(pDMA->dest);
      REG_DMA0CNT = pDMA->flags;
      // remove from list
      list_del(&pDMA->list);
    }
    /* update sprites */
    SpriteCopyToOAM();
  }
  REG_IF |= intFlag;
  SWI_ACKNOWLEDGE |= intFlag;
  /* turn them back on */
  REG_IME = 1;
}

#46870 - poslundc - Fri Jul 01, 2005 3:51 pm

1. All you want to do - at the moment, anyway - is get the interrupts working on hardware, right? Then stop trying to process multiple DMA transfers, update your shadow OAM, etc. First of all, you don't have time for any of that. Especially not in a C-coded ISR. If you're only updating the first 8 pixels it's very likely you won't see anything because you'll be halfway through the scanline before your ISR is complete. Get the interrupt working doing absolutely nothing other than setting a single value, like the backdrop colour (0x05000000), based on VCount, so you can see that you've got the basic code working and in action. Then you can build your other crap onto it and when stuff starts dropping out you'll know it's too much.

2. You are setting two acknowledgement bits, it seems: REG_IF and SWI_ACKNOWLEDGE. I don't know what SWI_ACKNOWLEDGE is, but all you need to do is set REG_IF.

3. Try assigning REG_IF directly (ie. REG_IF = ...) instead of using bitwise-or. I found that this made the difference when I wrote interrupt code.

4. Don't assign REG_IF its contents, since you might be getting other interrupts registering in it (and in fact you should be checking for these at the beginning of your routine). Assign to it the specific flag for the ISR that you've just completed, in this case 0x0002 for HBlank.

Dan.

#46880 - paladine - Fri Jul 01, 2005 6:45 pm

I made an empty routine that just returns immediately after acking the int. Still does not execute the interrupt code :( Maybe it's my compiler, i'm not sure. Funny thing is it works just fine on the emulator.

#46881 - paladine - Fri Jul 01, 2005 6:58 pm

OK well here's something strange - I recompiled it in ROM mode and it runs just fine. Apparently when I compile it for multiboot loading, it doesn't work.

#46907 - tepples - Fri Jul 01, 2005 11:02 pm

poslundc wrote:
2. You are setting two acknowledgement bits, it seems: REG_IF and SWI_ACKNOWLEDGE. I don't know what SWI_ACKNOWLEDGE is, but all you need to do is set REG_IF.

SWI_ACKNOWLEDGE might be what I've been calling BIOS_IF (0x03007ff8, mirrored at 32 KiB intervals up to 0x03fffff8). In order to use BIOS IntrWait() or VBlankIntrWait(), you need to let IntrWait() know which interrupts were just handled. This involves ORing the interrupts that you handled into BIOS_IF. Here's what a typical interrupt service routine looks like:

Code:
void isr(void)
{
  unsigned int interrupts = REG_IF;

  /* omit: handle each interrupt */

  BIOS_IF |= interrupts;  /* do not use = */
  REG_IF = interrupts;  /* do not use |= */
}

_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#46919 - paladine - Sat Jul 02, 2005 2:24 am

My handler routine works great. My problem is that the interrupt handler is not being called by the hardware when launched via multiboot. If I burn the image as a ROM to my flash card it plays fine. I just can't multiboot launch it on the real hardware.

#46921 - tepples - Sat Jul 02, 2005 2:33 am

Hmm... Your code seems to follow my example with one exception. Does it work if you change this line
Code:
  REG_IF |= intFlag;

to the following?
Code:
  REG_IF = intFlag;

If you're using more than one different interrupt, there might be a difference in behavior that only shows up when the serial is turned on or when the game's code starts on a different scanline.

Another thing that might help (in any mode except 3) is to put writes to the backdrop color (entry 0 of palette memory) at strategic points in the setup code, so that if it crashes, at least you know how far it got.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#46922 - LOst? - Sat Jul 02, 2005 2:52 am

paladine wrote:
My handler routine works great. My problem is that the interrupt handler is not being called by the hardware when launched via multiboot. If I burn the image as a ROM to my flash card it plays fine. I just can't multiboot launch it on the real hardware.

Have you checked the right options in the CRT0 file? Suck as multiple interrupts being ON? It may be some other options that might be turned ON or OFF.

#46929 - headspin - Sat Jul 02, 2005 5:50 am

Edit crt.s and make sure you have the following:

Code:
.equ __FastInterrupts, 1
@.equ __SingleInterrupts, 1
@.equ __MultipleInterrupts, 1


_Otherwise_ you need an interrupt table for your interrupts... example:

Code:

void HBlankInterrupt(void)
{
   unsigned long int regs = REG_IE;

   REG_IF |= INT_HBLANK;

   // do stuff;

   REG_IE = regs;

}

void (*IntrTable[])() =
{
   0, // v-blank
   &HBlankInterrupt(), // h-blank
   0, // vcount
   0, // timer0
   0, // timer1
   0, // timer2
   0, // timer3
   0, // serial
   0, // dma0
   0, // dma1
   0, // dma2
   0, // dma3
   0, // key
   0  // cart
};

void initInterrupt()
{
   REG_IME=0;
   REG_IE = INT_HBLANK | INT_VBLANK;
   REG_DISPSTAT = BIT04 | BIT03;
   REG_IME=1;            //enable interrupt
}

_________________
Warhawk DS | Manic Miner: The Lost Levels | The Detective Game

#46949 - wintermute - Sat Jul 02, 2005 2:21 pm

paladine wrote:
My handler routine works great. My problem is that the interrupt handler is not being called by the hardware when launched via multiboot. If I burn the image as a ROM to my flash card it plays fine. I just can't multiboot launch it on the real hardware.


How are you launching the multiboot app?

There are problems with some of the multiboot loaders around (f2a, xg2turbo & efa) which seem to stop interrupts working. xboo.com, Xboo Communicator & the mbv2 client don't suffer from this.

#46951 - paladine - Sat Jul 02, 2005 3:00 pm

I have a F2A and I launch it via the if2a utility that runs under Linux. That is somewhat comforting to hear that the multiboot loader can be buggy. At least I know my code is working as expected ;-)