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 > Simple Interrupt Question

#45807 - AkumaATR - Wed Jun 15, 2005 7:23 am

Working w/ interrupts is new to me. I am making the following assumptions:

If I turn off interrupts via REG_IME (interrupts won't reach the CPU), but that an interrupt (different one) occurs that I have turned on while handling the first interrupt, the second interrupt will still launch the interrupt handler (the source of the second interrupt is still trying to get it handled) after I'm done handling the first one and have returned.
In other words, I don't need care about priority or nested interrupts at this time. And I believe at this time that's fine for what I want to do.

So I'm wondering -- why can't REG_IME not be disabled at the beginning of the handler (turning off interrupts) to allow for nested interrupts to occur and be handled (why can't the handler just be called recursively) to handle all of the interrupts in LIFO manner? I'm sure there is an obvious answer to this regarding how interrupt/interrupt handlers work, but I can't think of it.

And my final question -- what happens if you enter the interrupt handler but that another interrupt occurs before the machine instruction comprised from the line REG_IME = 0x00 can actually be executed?

Thanks,
Jason

#45808 - DekuTree64 - Wed Jun 15, 2005 9:14 am

Yeah, interrupts will naturally 'nest' themselves that way, if they're all fast enough.

You actually don't need to disable IME in your handler at all, because there's a bit in the CPSR that also disables interrupts.

When one of the interrupt-generating things fires off (such as VBlank), it puts a 1 on whatever bit of IF that it's hooked to. Until you clear that bit by writing a 1 to it, it will stay set.
If that bit is set, and the corresponding bit in IE is set, and IME is set, and interrupts aren't disabled in the CPSR, an interrupt is generated.

When interrupted, the CPU does the following things:
* CPSR saves to SPSR_irq.
* CPU gets switched to IRQ mode, ARM instruction set, and IRQ/FIQ disabled (nothing on GBA/DS can generate an FIQ, so you can ignore that).
* Current pc saved to lr_irq.
* pc set to address 0x00000018 (the hardware IRQ vector).

On GBA, that puts you in the BIOS. It then branches to a small IRQ handler, which pushes r0-r3, r12 and lr onto the IRQ stack, loads the user handler address from address 0x03fffffc and branches to it.

Now your interrupt handler executes, still in IRQ mode with the IRQ/FIQ disable bits in the CPSR set. To do nested interrupts, you have to do lots of careful stuff here, including clearing that IRQ disable bit.

When you return from your handler, you're actually returning to that middle-man handler in the BIOS. It then pops all those registers back off the stack, and returns to where the interrupt originally happened.


Anyway, even when interrupts are disabled by the CPSR/IME/IE, things like VBlank will still put a 1 on their bit in IF. As soon as you reenable everything for that interrupt, it will happen.

So for example, if you're in the middle of a VBlank handler (with interrupts disabled in the CPSR), and a timer interrupt fires, it will set its bit in IF, but not actually do anything. As soon as you return from the VBlank handler, the timer will interrupt the CPU again.

The only time you need true nested interrupts is when you'll miss something entirely during a long handler.
Like, if your VBlank processing runs until 10 lines of the screen have been drawn, then HBlank interrupts on those 10 lines would have been missed. It would still trigger once as soon as your VBlank finishes, but those other lines have already been drawn and can't be changed.

Check this topic for discussion of nested handlers. It's pretty tricky stuff though, so don't worry too much about it yet.

And if you're feeling adventurous, check my post about how to bypass that little BIOS IRQ handler if you're working on the DS :)
Fun stuff if you play with it for a while and really get to understand how and why it all works.
_________________
___________
The best optimization is to do nothing at all.
Therefore a fully optimized program doesn't exist.
-Deku

#45841 - AkumaATR - Wed Jun 15, 2005 6:58 pm

Thanks Deku --

Really helpful info. None of the GBA materials I have mentioned that the disable bit in CPSR was automatically set before interrupt handling code executes.

I'm glad I won't have to deal with the nested IRQ stuff (at least yet) as it looked kind of messy.

Thanks Again,
Jason

#45852 - jormundgard - Wed Jun 15, 2005 9:12 pm

I have a slightly different question about interrupts - hopefully it's OK to shove it into this topic.

I'm using gcc and I made my own interrupt handler, and put the function pointer into 0x3007ffc (I think that's it). Even though I don't tell this function to restore the PC when it's finished, it seems to work fine. My question is: does the gcc compiler know to treat any function at 0x3007ffc as an interrupt handler, or is something in crt0.S taking care of this for me?

#45857 - tepples - Wed Jun 15, 2005 9:53 pm

The GBA BIOS takes care of it for you.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#45867 - jormundgard - Wed Jun 15, 2005 10:35 pm

I know there are bios routines to backup and restore certain registers (including the pc) but I'm having trouble understanding how the bios would know when the interrupt handler was finished, and when it should restore the pc. I figure the interrupt handler must end with some sort of jump statement to the bios restoration code. Is the return address taken from the stack in some way?

Maybe this is very standard stuff... sorry if I'm beating a dead horse. I guess that in the end I don't even need to worry about it!

#45868 - AkumaATR - Wed Jun 15, 2005 10:50 pm

"My question is: does the gcc compiler know to treat any function at 0x3007ffc as an interrupt handler, or is something in crt0.S taking care of this for me?"

That is the address of the register on the GBA that stores the address of the interrupt handler that is to be executed whenever any interrupt occurs.

See section 8: Hardware Interrupts at
http://www.cs.rit.edu/~tjh8300/CowBite/CowBiteSpec.htm#Windowing

for more info.

I think this information is mostly correct but I think there is a typo -- I think 8. should read:

8. It is the responsiblity of the code at that address to return once finished,
using BX LR <- not LR_irq

- Jason



Jason

#45870 - jormundgard - Wed Jun 15, 2005 11:02 pm

Reading this is what initially confused me, because I'm sure not telling the system to return! But I'm also not writing the assembly.

#45872 - AkumaATR - Wed Jun 15, 2005 11:17 pm

Saving/restoring the program counter when functions are called is automatic (in most higher-level languages) -- notice that if LR (the address for the next instruction in the BIOS, not in your code) is saved in the manner that the program counter is normally saved when calling traditional functions, the BIOS can continue running at LR at the end of the interrupt handler (the same way that program code can continue after a function call). The reason that "It is the responsiblity of the code at that address to return once finished, using BX LR" is explicitly stated there is that they are speaking an a machine code (ARM assemby) context (in which you are tasked with doing most everything).

- Jason

#45875 - jormundgard - Wed Jun 15, 2005 11:25 pm

I was starting to suspect something like this as I was reading along here. I'd really like to better understand how compilers do their job someday. Thanks for clarifying this, and I hope I didn't highjack your topic too badly :).

#45880 - AkumaATR - Wed Jun 15, 2005 11:46 pm

no worries. glad i could help a little. i am amazed with these forums/the people who frequent them thus far (in terms of how willing people are to assist others/response times) -- i look forward to helping more once i learn more!

- jason