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 > good start file for ARM STD/ADS?

#7783 - Lupin - Tue Jun 24, 2003 4:42 pm

where could I get one? Wich is the best one?

I'm not good enough to write my own :(

But I want to do interrupt handling myself (the start file should just set the address of my interrupt handler)

#7788 - DekuTree64 - Tue Jun 24, 2003 5:59 pm

I haven't used ARM SDT since I was first starting out and didn't have a clue what I was doing, so I can't help you with that, but for an interrupt handler, just set the address in your own code. Use
#define REG_INTERRUPT *(volatile u32*)0x3007FFC
And set REG_INTERRUPT to the address of your function and it will get called whenever an interrupt happens.

#7791 - Lupin - Tue Jun 24, 2003 6:32 pm

Do I actually need an start.s file? Seems like the GBA automatically calls a symbol named "start", so if i would name my entry function "start", the gba would perhaps call it, then I would just have to copy my rom into the gba memory.

Thanks for your hint, I will do it that way

#7812 - Jason Wilkins - Wed Jun 25, 2003 2:26 pm

You should start with assembly because that is the best way to create a ROM header and the only way to set the stack addresses. I'm don't know what the default values for the stack are on real hardware, but for VBA, the IRQ and USR stacks have the same address, so it wouldn't work unless you fix them up.

The interrupt handler does not have to be in crt0.S, as DekuTree64 pointed out. That is just another example of people not thinking and just copying what has been done before. It is easy enough to do using C code.

GBA does not call a symbol called _start, that is just the traditional name of the entry point. On some systems, like Linux which parse elf files, the OS does indeed call that symbol, on other systems, that start symbol is put where it will be called first. In other words, you have to make arrangements for _start to be where it will be called first because the system does not look for it.

The GBA just starts executing code at 0x08000000, so you make arrangments to put the start symbol there, it could just as easily be call robert. You don't even have to give it a symbol.

All this said, I did write a crt0.c, and it worked. I had to use inline assembly for the header and stack setting functions, and make sure the _start function was at the beginning of the file and make sure that crt0.o was the first thing on the linking command line. All of this can only be done because I know that the gcc toolchain will put _start first in the resulting file, there is nothing in the ANSI standard to guarantee that.

I used the naked, noreturn, and always_inline attributes to make the code almost as small as a hand written assembly routine. But, all this was just an exercise in learning, not something I would ever really use.

It would be easy to write a tool which translated ARM assembly syntax to gcc assembly syntax and visa versa. You could use flex, and it would take you about an hour or two.
_________________
http://devkitadv.sourceforge.net

#7814 - Lupin - Wed Jun 25, 2003 3:18 pm

thx for the input! I already know basic asm, but I don't know what the stack is used for and why I should initialize it... I don't think that I would be able to write an start file from scratch...

So, what exactly must actually be done within the start file?

#7817 - DekuTree64 - Wed Jun 25, 2003 4:35 pm

I think all that really HAS to be done is making a header, setting the stack pointers and and branching to your main function. Jeff's Crt0 includes al the neccessary stuff to run in multiboot mode, and handy stuff like if you run a multiboot ROM from a flash cart, it will copy it into EWRAM for you and branch to it. It also clears all the memory to 0, but unless your code depends on things being set to 0 to start with, that shouldn't really make a difference.
And if you're using functions in IWRAM, you need to copy in anything from the .iwram section. From Jeff's:
Code:

@ Copy internal work ram (iwram section) from LMA to VMA (ROM to RAM)
ldr     r1,=__iwram_lma
ldr     r2,=__iwram_start
ldr     r4,=__iwram_end
bl      CopyMemChk

Those are defined by the linkscript, so basically what that does is a memcpy(__iwram_start, __iwram_lma, __iwram_end - __iwram_start);

And it sets the IRQ handler too, but I use my own, since I find it handy in an RPG to have like one interrupt table for walking around maps, and a different table for battles. If you have the irqTable variable just a pointer to a table, you can do irqTable = mapIrq; to switch between them without having to copy the whole thing to a temporary place. Pretty cool, though not totaly neccessary.

Just read through Crt0.S every now and then and eventually it will start to make sense. At least it did for me, I used to be completely confused by it, but now it pretty much makes sense.

#7820 - Jason Wilkins - Wed Jun 25, 2003 5:48 pm

You should read my crt0.S for DKA5, I am pretty damn proud of it.
_________________
http://devkitadv.sourceforge.net

#7821 - Lupin - Wed Jun 25, 2003 7:07 pm

Jason: What files do I really need to compile using DKA5?

Deku: Since I'm trying to use ADS the __iwram_XXX symbols would perhaps not be defined... documentation for ADS is really bad :(

I tried to load the address of an interrupt handler function into the register, but somehow it doesn't work correctly, could you please tell me what i did wrong here?

#define REG_INTERUPT (*(vu32*)0x3007FFC)
typedef void (*fp)(void);

fp inthndlr;
inthndlr = HandleInterrupt;
REG_INTERUPT = (u32)inthndlr;

void HandleInterrupt() {
switch (REG_IF) {
case INT_VBLANK:
VBLANK();
REG_IF = INT_VBLANK;
case INT_KEYBOARD:
KEYBOARD();
REG_IF = INT_KEYBOARD;
}
}


sorry for asking so much dumb stuff :(

#7822 - Jason Wilkins - Wed Jun 25, 2003 8:10 pm

You need to have a pointer to a pointer to a function.

The location, scrictly speaking, is not a register. Its location and function are determined totally by software, so it is just a variable.

It does not have to be volatile. Only values which can be changed by factors other than the current thread of execution or values which are different if you read them as opposed to write them need to be volatile (some hardware, including the GBA, saves address space by overlapping read-only and write-only registers).

So, the interrupt vector is not volatile. Of course, you could make it volatile by having an interrupt handler change it, but this rule applies to all variables which you change in an interrupt handler if you read them from code that may be interrupted by that handler.

Code:

typedef void (*interrupt_handler_t)();
typedef interrupt_handler_t *interrupt_handler_ptr_t;

#define INTERRUPT_VECTOR (*((interrupt_handler_ptr_t)0x03007FFC))

void my_interrupt_handler(void)
{
}

int main(void)
{
   INTERRUPT_VECTOR = my_interrupt_handler;
   return 0;
}



DevKit Advance can be downloaded here: http://sourceforge.net/project/showfiles.php?group_id=673150

Or, you could have followed the link in my sig and found it ^_^
_________________
http://devkitadv.sourceforge.net


Last edited by Jason Wilkins on Wed Jun 25, 2003 8:20 pm; edited 1 time in total

#7823 - DekuTree64 - Wed Jun 25, 2003 8:15 pm

Yea, just leave out that IWRAM stuff until you figure out how to put code in different places with ADS.
And for the interrupt handler, you don't need to make a function pointer first, but that shouldn't cause any problems either. Just use REG_INTERRUPT = (u32)HandleInterrupt;
Are you compiling that to ARM? When an interrupt happens, the CPU automatically switches to ARM mode, so if you're using THUMB, it will get confused.

#7824 - Jason Wilkins - Wed Jun 25, 2003 8:23 pm

I think it is a good rule in C programming to never use type casts unless you have to. It is just sloppy, and contributed to Lupin not understanding what exactly is going on. Would he have had to make a second post asking what he was doing wrong if no obsfucating cast had been used? Why say something you don't mean?

You could declare int_handler_ptr_t as

Code:

typedef void (**int_handler_ptr_t)(void);


if you want to, I just broke it into two declarations so that it is clearer.
_________________
http://devkitadv.sourceforge.net

#7825 - DekuTree64 - Wed Jun 25, 2003 11:43 pm

Jason Wilkins wrote:
I think it is a good rule in C programming to never use type casts unless you have to. It is just sloppy, and contributed to Lupin not understanding what exactly is going on. Would he have had to make a second post asking what he was doing wrong if no obsfucating cast had been used? Why say something you don't mean?


Actually, my last post was a reply to Lupin's, not yours. You just got there first while I was typing it.
But when did I ever use a confusing typecast? Was it that #define REG_INTERRUPT *(volatile u32*)0x3007FFC? I prefer using the u/s 8/16/32 types for pretty much everything, since that makes it more clear exactly how the compiler will treat them. But if you think typecasts are sloppy, you'd most certainly hate my code^_^ I use them a lot, just to be sure exactly what's going on.
But anyway, sorry for any confusion I've caused

#7848 - Jason Wilkins - Thu Jun 26, 2003 3:50 pm

Excuse me then.

The typecast for 0x03007FFC is necessary, there are a few other ways to do it, but none of them involve C.

The problem is saying that it is a u32 when it is not. It is a pointer to a function. Why chose a u32? Why not an i32? or an int? or hell, why not a float? They are all equally arbitruary and require you to use a cast everytime you assign it.

Imagine if I used declared an integer as a float and then cast it to an int every time I used it. Of course, i would know 'how the compiler is going to treat it' but it is still silly.

Actually, I think it is pointless to have the uNN and iNN types. Everyone knows that an int is 32 bits and a short is 16 bits, no need to make it explicit. BUT, it does save some typing, and it is all just a matter of opinion. That was not the source of my comment.

My point was that you shouldn't create a declaration that forces you to use a typecast every single time, you may as well have cast it into strawberry_jam_t, as long as strawberry_jam_t was 32-bit it would work, but why?

If you use type casts a lot, it is not just a matter of 'hating' your code. I do not see this as a matter of opinion, it is more like having an objective reason to think that you don't know what you are doing. Just like you would say that a person who doesn't know how to punctuate a sentence is probably not a good writer.

A static type cast turns off the compiler's ability to check for errors, by using them you are just asking for it. It is a sign that you have not thought about what you are doing enough.

Of course, I can't really make any specific judgements about YOUR code DekuTree64, these are just general statements about type casts. I would have to see your code to make any real call on it. It is likely that you are just talking about using type casts for changing integer literals to pointers which is totally necessary for low level hardware programming. Maybe it is something else, but if I see code with a lot of type casts, it is the same to me as seeing a lot of gotos (not 1 goto, but lots).
_________________
http://devkitadv.sourceforge.net