Accessing C struct members from Assembly?

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems.

Moderator: Moderators

Post Reply
User avatar
gauauu
Posts: 652
Joined: Sat Jan 09, 2016 9:21 pm
Location: Central Illinois, USA
Contact:

Accessing C struct members from Assembly?

Post by gauauu » Fri Nov 16, 2018 1:28 pm

I'm curious if cc65/ca65 has a method for accessing C struct members by name from assembly. I looked through the docs and didn't find anything.

Here's an example of what I mean:

Code: Select all

struct Foo {
  char x;
  char y;
  char z;
}

struct Foo myFoo;
Now later in assembly, I want to access myFoo.y without knowing the position of y in Foo. If I know that it's the 2nd member, it's easy to do:

Code: Select all

 lda _myFoo+1

But is there any notation for accessing it by name y? I know there are also structs in ca65, but I don't know any way to make a struct available to both C and assembly without building my own preprocessor to generate the code for one or the other.

(I know I can completely rework things, using structs of arrays instead of an array of structs, etc. I know there's many reasons not to do what I'm doing in 6502. I'm not asking about those here. I'm just curious if there's a way to let assembly know about the ordering of members so that if I add a member w in Foo, before x, my assembly code won't need to be updated.)

User avatar
dougeff
Posts: 2614
Joined: Fri May 08, 2015 7:17 pm
Location: DIGDUG
Contact:

Re: Accessing C struct members from Assembly?

Post by dougeff » Fri Nov 16, 2018 1:50 pm

You could define some constants to make the code clearer.

x = 0
y = 1
z = 2

So
lda _myFoo+1
becomes
lda _myFoo+y

*x, y, z are not good variable names
Last edited by dougeff on Fri Nov 16, 2018 2:45 pm, edited 1 time in total.
nesdoug.com -- blog/tutorial on programming for the NES

User avatar
gauauu
Posts: 652
Joined: Sat Jan 09, 2016 9:21 pm
Location: Central Illinois, USA
Contact:

Re: Accessing C struct members from Assembly?

Post by gauauu » Fri Nov 16, 2018 2:18 pm

dougeff wrote:You could define some constants to make the code clearer.

x = 0
y = 1
z = 2

So
lda _myFoo+1
becomes
lda _myFoo+y
Yeah, that's what I've done in the past. It still requires me modifying all the constants if I insert a member w. Worst comes to worst, I can write a pre-processor that generates all those constants for me, but I'd hate to do that if cc65 already provides a mechanism to do this.
*x, y, z are not be good variable names
Absolutely, they're just placeholders to illustrate the problem. Like Foo.

yaros
Posts: 145
Joined: Tue Aug 28, 2018 8:54 am
Location: Edmonton, Canada

Re: Accessing C struct members from Assembly?

Post by yaros » Fri Nov 16, 2018 3:58 pm

I know you can use "offsetof" when you use inline assembly. But it doesn't seem that cc65 export symbols with the offsets.

Code: Select all

        typedef struct {
            unsigned char x;
            unsigned char y;
            unsigned char color;
        } pixel_t;
        static pixel_t pixel;
        __asm__ ("ldy #%b", offsetof(pixel_t, color));
        __asm__ ("lda %v,y", pixel);
Edit:

I looked around, there doesn't seem to be a way to output arbitrary text into the assembler file. __asm__ always validates to make sure it it only has mnemonics.

yaros
Posts: 145
Joined: Tue Aug 28, 2018 8:54 am
Location: Edmonton, Canada

Re: Accessing C struct members from Assembly?

Post by yaros » Fri Nov 16, 2018 5:27 pm

Okay, so here we go. This is the time of wonderful hacks, and you have every right to throw a boot at me. I don't think it is how you should do it, but you CAN do it.

Lets imagine you have your header where you only define structs.

struct.h

Code: Select all

typedef struct MyFoo
{
    char i;
    char d;
} MyFoo;
You use it in your C code and everything is fine. Now you need offsets. And you can start throwing tomatoes.

struct.h

Code: Select all

#if !ASSEMBLY
    #define structfield(type, asmtype, name) type name;
    #define beginstruct(name) typedef struct name {
    #define endstruct(name) } name;
#else
    #define structfield(type, asmtype, name) name asmtype
    #define beginstruct(name) .struct name
    #define endstruct(name) .endstruct
#endif


beginstruct(MyFoo)
    structfield(char, .byte, i)
    structfield(int, .word, p)
endstruct(MyFoo)

If you include it into the C file, everthing will be fine. For assembler, convert this header into intermediate ".i" file using the following line:

Code: Select all

cc65 struct.h -DASSEMBLY -E
Which will output:

Code: Select all

.struct MyFoo
i .byte
p .word
.endstruct

User avatar
Banshaku
Posts: 2328
Joined: Tue Jun 24, 2008 8:38 pm
Location: Fukuoka, Japan
Contact:

Re: Accessing C struct members from Assembly?

Post by Banshaku » Fri Nov 16, 2018 7:30 pm

There is no automated way, unfortunately (except for maybe the hack above but I don't know if there is any drawback to do so :lol:).

What I do is I have a file that contains the definition, again, for the asm side and I keep them in sync when updated. If you don't have many structure to share it may not be a big issue. An example of what I used:

C side:

Code: Select all

typedef struct {
	const unsigned char* frameCount;
	const unsigned char* const *frameList;
} animInfo_t; 

typedef struct {
	unsigned char index;           // index of the frame to be shown
	unsigned char count;           // current count for next frame
	unsigned char maxCount;        // how long is the frame
	const unsigned char* current;  // the current frame to be shown
} frameInfo_t;

// player's specific information
typedef struct {
	int x;						   // location inside stage. May need negative, not sure yet.
	int y; 						   // vertical location, now 16 bits for checking bound outside screen
	unsigned char direction;       // which direction facing, left or right
	unsigned char priority;		   // if shown priority is front or background
	unsigned char outOfBound;	   // if went out of bound in a direction (up/down/left/right) based on the limit of the map
	unsigned char state;	       // state of actor (for player, this means animState)
	unsigned char subState;        // state that modify current state (ex: using weapon while doing somethings)
	unsigned char previousState;   // previous states
	unsigned char health;		   // tentative: value for health
	unsigned char invicibleTimer;  // flag & counter for invincible state (> 0, in that state)
	unsigned char weapon; 		   // which weapon was selected
	frameInfo_t frame;
	animInfo_t anim;
} player_t;
Asm side (started to convert recently, maybe issue in it)

Code: Select all

;
; Info regarding animation for the currently selected anim
;
.struct FrameInfo
    index       .byte 
    count       .byte
    maxCount    .byte
    current     .word 
.endstruct

;
; animation info
;
.struct AnimInfo
    frameCount  .word
    frameList   .word
.endstruct

;
; Player information definition
;
.struct Player
    posX        .word
    posY        .word
    direction   .byte
    priority    .byte
    outOfBound  .byte
    state       .byte
    subState    .byte
    previousState .byte
    health      .byte
    invicibleTimer .byte
    weapon      .byte
    frame       .tag FrameInfo
    anim        .tag AnimInfo
.endstruct
Something like that.

Then in asm you just use the struct, like expected:

Code: Select all


lda _gplayer + Player::state


yaros
Posts: 145
Joined: Tue Aug 28, 2018 8:54 am
Location: Edmonton, Canada

Re: Accessing C struct members from Assembly?

Post by yaros » Fri Nov 16, 2018 8:30 pm

Banshaku wrote:There is no automated way, unfortunately (except for maybe the hack above but I don't know if there is any drawback to do so :lol:).
I don't think there are any dwarbacks, except it is just ugly. What would be better, is instead of using C89 macroses, is to use powerfuul ca65 macros system and generate C89 header file. It will be a little cleaner, but I was coming from the assumption that C89 is the primary where ca65 is secondary, so generation was coming from C header files.

User avatar
gauauu
Posts: 652
Joined: Sat Jan 09, 2016 9:21 pm
Location: Central Illinois, USA
Contact:

Re: Accessing C struct members from Assembly?

Post by gauauu » Fri Nov 16, 2018 8:34 pm

Yeah, the C has more type information than what would be in the assembly (signed vs unsigned, whether it's an int vs a pointer), so my intention was to go that direction.

User avatar
gauauu
Posts: 652
Joined: Sat Jan 09, 2016 9:21 pm
Location: Central Illinois, USA
Contact:

Re: Accessing C struct members from Assembly?

Post by gauauu » Fri Nov 16, 2018 8:36 pm

Either way, thanks for the suggestions. I appreciate the input.

yaros
Posts: 145
Joined: Tue Aug 28, 2018 8:54 am
Location: Edmonton, Canada

Re: Accessing C struct members from Assembly?

Post by yaros » Fri Nov 16, 2018 8:42 pm

gauauu wrote:Yeah, the C has more type information than what would be in the assembly (signed vs unsigned, whether it's an int vs a pointer), so my intention was to go that direction.
True, but you can create ca65 macroses like. Just because ca65 macroses are WAY more powerful, teples even tried (or did) create assembler using them.

Code: Select all

    STRUCT "MyFoo"
		FIELD "char", x
		FIELD "unsigned int", y
	ENDSTRUCT
That will generate either C struct or ASM struct depending on the defined symbol.

Code: Select all

.macro FIELD type, name
	.ifdef ASSEMBLER
		.if .match({type}, "char")
			name .byte
		.elseif .match({type}, "int") .or .match({type}, "unsigned int") .or .match({type}, "uint16_t")  .or .match({type}, "int16_t")
			name .word
		.endif
	.else
	.endif
.endmacro

Post Reply