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.

ASM > Access data from structs?

#27519 - ProblemBaby - Thu Oct 14, 2004 6:01 pm

Is it possible in asm to access data that is putted in a C struct.

Code:

LDR r0, =GlobalStruct
LDR r1, [r0, GlobalStruct.Var]

or something
it isnt fun to search the exact address for each variable
and especially not if Ive to add one at the top or make a buffer bigger.

Plz help!

#27520 - poslundc - Thu Oct 14, 2004 6:15 pm

There is no built in or really simple method if your structs keep changing a lot. This topic links to a more sophisticated and somewhat complicated method of sharing structs between C and ASM; it's what I use for all of my projects that require it.

Dan.

#27528 - ProblemBaby - Thu Oct 14, 2004 7:45 pm

I didnt exactly understand but I think its stupid that it just doesnt exist a built in function like offsettof(ASTRUCT, Var).
And then put it in the code

#27530 - sgeos - Thu Oct 14, 2004 7:57 pm

ProblemBaby wrote:
I didnt exactly understand but I think its stupid that it just doesnt exist a built in function like offsettof(ASTRUCT, Var).
And then put it in the code

Why not write a macro that does exactly that?

-Brendan

#27531 - sajiimori - Thu Oct 14, 2004 8:12 pm

In C it is trivial:
Code:
#define STRUCT_OFFSET(type, elem) ((u32)&((type*)0)->elem)
An assembler OTOH has no way of knowing that information.

#27533 - poslundc - Thu Oct 14, 2004 8:39 pm

The general strategy in the article I referenced is to write a C program that generates another header file with these offsets precalculated. This header file is then #included in your ASM project, and you write your makefile so that if any of the struct-containing header files change then the program runs and recreates the ASM header file.

It is a complicated solution, and it requires GCC both for the ARM and for your native computer, and GCC for your computer's architecture must generate structs the same way that the ARM-targetted GCC does (which should be the case on most computers; it's like that in Mac OS anyway). But until they come out with a version of GAS that features better C compatibility, it's the best way I'm aware of.

Dan.

#27539 - keldon - Fri Oct 15, 2004 12:42 am

A macro should be able to do that (providing your assembler has the correct features, which it may not).

EDIT: It can be done easily in the assembler I use, but it is unlikely it can be done in any other. Sometimes I do forget.

#27541 - sajiimori - Fri Oct 15, 2004 1:37 am

How do you do it in your assembler, out of curiosity?

#27549 - keldon - Fri Oct 15, 2004 11:35 am

The macro is [enum | {#1 #x} | #+1]

#means for parameter number, so #1 is parameter 1

#+1 means loop from first to last parameter, adding 1 to all parameter numbers, so #1 really means #2

#x is the loop number, which comes into effect when you have more than one parameter

{} is converted to [] when it is unfolded, this is because variables and constants use square brackets also.
---
So enum startState endState keyState unfolds as:
[startState 1] ; Loop 1 (#x = 1, #1 points to param 1)
[endState 2] ; Loop 2 (#x = 2, #1 points to param 2)
[keyState 3] ; Loop 3 (#x = 3, #1 points to param 3)

#27553 - poslundc - Fri Oct 15, 2004 1:50 pm

Maybe I'm missing something obvious but I don't see how that works to make C structs compatible with assembly code.

Dan.

#27560 - keldon - Fri Oct 15, 2004 3:29 pm

oops, completely forgot I was doing structs and not enums

well first I'd make some defines
Code:
[u16 2 ; u16 is 2 bytes
u32 4] ; u32 is 4 bytes

There are more complex features to the macros set. &1 means compile time variable 1
Code:
[Struct | { &1=0 | &2=#1 | #1: StructVariables #2>L | } ]
[StructVariables | &2.#2 &1 | &1=&1+#1 | #+2]


So Struct myStruct u16 Egg, u32 Stone, u16 Fire unfolds as
Code:
[myStruct:
myStruct.Egg 0
myStruct.Stone 2
myStruct.Fire 6]

#27561 - jma - Fri Oct 15, 2004 3:36 pm

Perhaps this was covered in the article that poslundc posted. However, the main reason this is a difficult task is because the C spec doesn't specify that members of a struct must be offset in order -- nor that alignment issues are handled properly. For example:

Code:
typedef struct my_struct {
  char b;
  u32 i;
} my_struct;


How many bytes does my_struct occupy? It isn't know without a sizeof operation. It could be 5 or 8. If b is first (which is what most compilers would do -- it's easier) then it could very well be 8 if the compiler word aligns i. But there is nothing in the spec preventing the compiler from putting i first (because it is already word aligned) followed by b -- making the structure only 5 bytes in size.

To get around this, typically compilers allow for specific #pragma statements that allow you to specify (for example) pack to force the compiler to not pad out structures, show the padding, etc. This is all very compiler and platform specific, which is what makes it difficult to do.

This is why many times you'll see structures that are defined that have dumby data in them to pad out memory alignment issues:

Code:
typedef struct my_struct {
  char b;
  char pad[3];
  u32 i;
} my_struct;


I should re-iterate, though, that most compilers will compile structure offsets in-order (eg, b first, then pad, finally i). For your specific situation (the GBA and GNU) you might be very aware of what is happening under-the-hood and take advantage of it. But many, very-optimizing compilers will reorganize your structure for you.

Jeff
_________________
massung@gmail.com
http://www.retrobyte.org

#27562 - poslundc - Fri Oct 15, 2004 3:49 pm

This is part of why keldon's solution is no good for me... after all, GAS provides a .struct directive; that isn't the problem. The two problems are:

1. How GCC constructs/optimizes the struct

2. Keeping your ASM structs consistent with changes to your C structs

The technique that sajimori shows earlier in this thread works to solve the first problem (there is also an offsetof macro that does the same thing; I forget where it's defined but it's in that article I linked to).

The second problem is more compelling, because you can declare your structs using whatever directives your assembler provides for you, but it means you then have to keep track of your structures in two places, which is a real pain.


My solution (which I also briefly explained further up) is to write an auxillary program in C for my computer (as opposed to for the GBA) that #includes my various header files that have structs in them. This program's function is to output a header file ("asmheaders.h") with a bunch of #defines in it that have all of the necessary offsets and sizes of the structures I'm interested in for my ASM routines.

Then my makefile has a dependency that lists those header files and checks them whenever it builds, and runs the auxillary program and rebuilds the ASM files if the header files have changed.

It's a bit of a pain to set up and adding new structs means changing the source of the auxillary program (adding new header files to it means changing the makefile as well), but it's a very smooth system once everything is set up and working properly.

The only caveat to this system is that GCC targetted for your computer has to generate its structs using the same rules that it does when targetted for the ARM. Which thankfully it does, at least on the Mac and probably on the PC as well.

Dan.