It is currently Mon Jun 24, 2019 3:02 pm

All times are UTC - 7 hours





Post new topic Reply to topic  [ 10 posts ] 
Author Message
PostPosted: Fri Nov 16, 2018 1:28 pm 
Offline
User avatar

Joined: Sat Jan 09, 2016 9:21 pm
Posts: 618
Location: Central Illinois, USA
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:
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:
 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.)

_________________
My games: http://www.bitethechili.com


Top
 Profile  
 
PostPosted: Fri Nov 16, 2018 1:50 pm 
Offline
User avatar

Joined: Fri May 08, 2015 7:17 pm
Posts: 2529
Location: DIGDUG
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

_________________
nesdoug.com -- blog/tutorial on programming for the NES


Last edited by dougeff on Fri Nov 16, 2018 2:45 pm, edited 1 time in total.

Top
 Profile  
 
PostPosted: Fri Nov 16, 2018 2:18 pm 
Offline
User avatar

Joined: Sat Jan 09, 2016 9:21 pm
Posts: 618
Location: Central Illinois, USA
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.

Quote:
*x, y, z are not be good variable names


Absolutely, they're just placeholders to illustrate the problem. Like Foo.

_________________
My games: http://www.bitethechili.com


Top
 Profile  
 
PostPosted: Fri Nov 16, 2018 3:58 pm 
Offline

Joined: Tue Aug 28, 2018 8:54 am
Posts: 143
Location: Edmonton, Canada
I know you can use "offsetof" when you use inline assembly. But it doesn't seem that cc65 export symbols with the offsets.

Code:
        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.


Top
 Profile  
 
PostPosted: Fri Nov 16, 2018 5:27 pm 
Offline

Joined: Tue Aug 28, 2018 8:54 am
Posts: 143
Location: Edmonton, Canada
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:
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:
#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:
cc65 struct.h -DASSEMBLY -E


Which will output:

Code:
.struct MyFoo
i .byte
p .word
.endstruct


Top
 Profile  
 
PostPosted: Fri Nov 16, 2018 7:30 pm 
Offline
User avatar

Joined: Tue Jun 24, 2008 8:38 pm
Posts: 2302
Location: Fukuoka, Japan
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:
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:
;
; 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:

lda _gplayer + Player::state



Top
 Profile  
 
PostPosted: Fri Nov 16, 2018 8:30 pm 
Offline

Joined: Tue Aug 28, 2018 8:54 am
Posts: 143
Location: Edmonton, Canada
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.


Top
 Profile  
 
PostPosted: Fri Nov 16, 2018 8:34 pm 
Offline
User avatar

Joined: Sat Jan 09, 2016 9:21 pm
Posts: 618
Location: Central Illinois, USA
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.

_________________
My games: http://www.bitethechili.com


Top
 Profile  
 
PostPosted: Fri Nov 16, 2018 8:36 pm 
Offline
User avatar

Joined: Sat Jan 09, 2016 9:21 pm
Posts: 618
Location: Central Illinois, USA
Either way, thanks for the suggestions. I appreciate the input.

_________________
My games: http://www.bitethechili.com


Top
 Profile  
 
PostPosted: Fri Nov 16, 2018 8:42 pm 
Offline

Joined: Tue Aug 28, 2018 8:54 am
Posts: 143
Location: Edmonton, Canada
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:
    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:
.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


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 10 posts ] 

All times are UTC - 7 hours


Who is online

Users browsing this forum: No registered users and 3 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group