Hi!

This is my code:


Code:

   char player_name[3][11] = {{'x','x','P','l','a','y','e','r',' ','1',0}, {'x','x','P','l','a','y','e','r',' ','1',0}, {'x','x','P','l','a','y','e','r',' ','1',0}};
   PrintXY(3, 3, player_name[0], TEXT_MODE_NORMAL, TEXT_COLOR_BLACK);


I have an array (player_name). It contains the player name of 3 players.
The problem is that when I try to print player_name[0], or player_name[2], I get this error message:



But it is possible to print player_name[1]!

And I can't understand why!

OK, I can use other methods to print these strings from an array.

If anyone can find the reason of this error, please post it! I'm interested
balping wrote:
Looks like a misaligned load, and it works out that (since your members are an odd number of bytes long) the second member is aligned, while the others are not.

I'd do this.

Code:
const char *player_name[] = {
    "xxPlayer 1",
    "xxPlayer 1",
    "xxPlayer 1"
};
Not sure what else you will try to do with this, but it may need changing if you expect to modify the strings at runtime.
Thanks, it works!
As we discussed on IRC, this is an interesting tidbit that you have found, and (Tari's words) may explain what those first two bytes of a string are. Since there's an alignment issue, they aren't being read as two bytes, but potentially as a 16-bit short. The question is, what does that number represent? Out of curiosity, are the messages that we've been getting from PrintXY_2() prefixed with anything interesting?
I played with ways to do this to see how GCC reacts, and it supports the theory that PrintXY interprets the first couple bytes as an integer.

With the 11 by 3 array, the assembly is something like this:
Code:
    .section .rodata
.LC0:
    .byte 120
    .byte 120
    .byte 80
[lots of literals snipped]

    .text
    .global _main
_main:
    ! function prologue omitted (40 bytes in stack frame)
    ! memcpy from LC0 to stack frame (initialize array)

    ! Parameter setup to PrintXY
    ! (r15 is the stack pointer)
    mov r15,r1
    add #7,r1
    mov r1, r2    ; blame conservative optimizer settings for this oddity
    ! sp is always 4-byte aligned, so note that player_name is not aligned since we added 7!
    add #11, r2
    ! r2 now points to player_name[2]
    ! set up remaining parameters and call PrintXY
When declaring it as an array of const char *, GCC forces alignment on the strings for some reason:
Code:
    .section .rodata
    .align 2
.LC0:
    .string "xxPlayer 1"
    .align 2
.LC1:
    .string "xxPlayer 2"
    .align 2
.LC2:
    .string "xxPlayer 3"
I was actually a bit surprised by how inefficient that ended up being, because it copies pointers to these strings into the stack frame:
Code:
! in main, prologue omitted
    mov r15, r1
    add #4, r1
    mov.l .L3, r2
    mov.l r2, @r1
    mov r15, r1
    add #4, r1
    mov.l .L4, r2
    mov.l r2,@(4,r1)
    ! You get the idea

.L3: .long .LC0
.L4: .long .LC1
! etc


This leads to some good considerations for writing efficient code. When your string array is jagged (they're of differing lengths), be sure to qualify everything const:

Code:
const char *const player_names[] = {
    "xxPlayer 1",
    "xxPlayer 2",
    "xxPlayer 3"
};
This saves around 16 instructions because the pointers don't get copied into your stack frame. If you want the ultimate in memory efficiency and your strings are all very close to the same size, specifying a 2d array is best:
Code:
const char player_names[][11] = {
    "xxPlayer 1",
    "xxPlayer 2",
    "xxPlayer 3"
};
This requires that you specify the major dimension, but it can be longer than a given string without causing any harm (just wasting the leftover space).

Interestingly, the "array of pointers" approach continues to enforce alignment on the strings with the extra const qualifiers. I'd be interested to see if the non-jagged variant with const qualifiers crashes in PrintXY due to alignment issues, as that would support the theory that it's interpreting the first two bytes as a number.
Fascinating investigation, Tari, and some interesting conclusions regarding best practices. Would you say therefore that it is safe to try to make as many constants marked "const" as possible, considering I already do it for all my sprites and images in order to get them into the correct section of the binary?
KermMartian wrote:
Would you say therefore that it is safe to try to make as many constants marked "const" as possible, considering I already do it for all my sprites and images in order to get them into the correct section of the binary?
Definitely. Adding all the qualifiers that make sense can only make the generated code better, and it's a significant consideration for our SH processor, since it's pretty inefficient at getting absolute addresses.

For that matter, qualifying your pointers 'restrict' where it makes sense to do so will help the optimizer too, but less noticeably. Without checking it, I suspect doing so on inputs to routines that both read and write to VRAM could shave a few instructions off your innermost loops, though.
sort-of not on topic, but:

I know that for the z80 calcs, hand-optimized assembly proved to be a big help for sprite copying. Would it also be helpful for the prizm as well? I only assume that the machine code that gcc cranks out can be optimized in some way.

Also, what do you mean by "it's pretty inefficient at getting absolute addresses. "?
Hmm, and today I learned what the 'restrict' qualifier actually means, thanks to your post and Wikipedia. I would think that the vast majority of the time, my pointers would indeed be pointing at distinct addresses, unless I was dealing with data structures like linked lists.
Same here. I will be sure to try and use that.
AHelper wrote:
sort-of not on topic, but:

I know that for the z80 calcs, hand-optimized assembly proved to be a big help for sprite copying. Would it also be helpful for the prizm as well? I only assume that the machine code that gcc cranks out can be optimized in some way.
According to popular wisdom, modern optimizing compilers (read: not SDCC) are as good as ASM experts painstakingly hand-tuning code. The conference I recently attended in Korea, APSys 2012, included an excellent talk demonstrating exactly that. Cf. Correct, Fast, Maintainable: Choose Any Three!.
Doubly so if you turn on LTO (alternate term: IPA)! Cross-module code analysis at link time allows the compiler to throw out all the ABI assumptions it's forced to make when considering modules in isolation and do significant inlining.
  
Register to Join the Conversation
Have your own thoughts to add to this or any other topic? Want to ask a question, offer a suggestion, share your own programs and projects, upload a file to the file archives, get help with calculator and computer programming, or simply chat with like-minded coders and tech and calculator enthusiasts via the site-wide AJAX SAX widget? Registration for a free Cemetech account only takes a minute.

» Go to Registration page
Page 1 of 1
» All times are UTC - 5 Hours
 
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 vote in polls in this forum

 

Advertisement