Jump to content
  • entries
    657
  • comments
    2,692
  • views
    898,498

Minor C issue, any ideas?


SpiceWare

1,382 views

Finally getting some work done on SpiceC and have run into an issue. Storing graphic data in the C code like this:
blogentry-3056-0-34637100-1538186589_thumb.png

Lets you create a list of images like this:

const unsigned char* Images[] ={  Timmy,  RedBox};




However, each entry in that list takes up 4 bytes (size of a pointer) which is twice as much space as needed as when I set up the graphic data via dasm files.

Since the data ends up in the first 32K of the addressing space, I should be able to do something like this:

 

const unsigned short int Images[] ={  Timmy,  RedBox};




or this:

 

const unsigned short int Images[] ={  (unsigned short int)Timmy,  (unsigned short int)RedBox};




However, I get compile time errors:
game.c:41:3: error: initializer element is not constantgame.c:42:3: error: initializer element is not constant




Anybody have any ideas? Thanks!

10 Comments


Recommended Comments

Doesn't work:

 

game.c:12:7: error: unknown type name ‘byte’

Most likely one of the header files used by your CV project has this:

#define byte unsigned char

or this:

#define byte char

in it. Thanks anyway!

Link to comment

I’ve typicslly handled things like this by declaring one array and then multiplying by a fixed offset to get to image I want:

 

const unsigned char Images[] = {
// Timmy
0b00111100,
0b01011010,
0b00100100,
0b00011000,
0b01111110,
0b10111101,
0b10111101,
0b00111100,
0b00100100,
0b00100100,

// RedBox
0b11111111,
0b10000011,
0b10111101,
0b10111101,
0b10000011,
0b10110111,
0b10111011,
0b10111101,
0b11111111,

// more images here
};

 

You could also declare it as a two dimensional array if you wanted to hide the multiply.

  • Like 1
Link to comment

Images are different heights so a fixed offset won't work. Consistent heights would work, just pad with zeros, but that'll result in more on-screen flicker when multiplexing all the sprites thru the 2600's two players.

 

I've been thinking about 2 ways, one similar to your suggestion and another as I did it for my other projects.

 

C using 3 tables:

  • Images - all image data as in your example
  • Offsets - starting point for each image so Offset[0] = 0, Offset[1] = 10, Offset[2] = 19, etc.
  • Heights - Height[0] = 10, Height[1] = 9, etc.
6507 asm using 2 tables:
  • Images = each 2-byte entry points to a block of image data, Images[0] = Timmy, Images[1] = RedBox
  • Heights - Height[0] = 10, Height[1] = 0
Need to weight the pros and cons of each method. Both are difficult for the end-user so I think I'll end up creating a utility to convert a graphic data file, containing something like, this to whatever I decide on:

 

Timmy
      0b00111100
      0b01011010
      0b00100100
      0b00011000
      0b01111110
      0b10111101
      0b10111101
      0b00111100
      0b00100100
      0b00100100

RedBox
      0b11111111
      0b10000011
      0b10111101
      0b10111101
      0b10000011
      0b10110111
      0b10111011
      0b10111101
      0b11111111

The values could be 0b or % for binary, 0x or $ for hex, no prefix for decimal, and maybe even my old zz notation that I used to make the images visible within the data before I discovered how to have jEdit colorize binary (see the last image).

 

The utility would overlap data to save space like Thomas' DOOD utility. An example of what that does is this (using the aforementioned zz notation):

BirdG: ; @$28 = 40
        .byte zz___X____
        .byte zz________
        .byte zz_XX_X___
        .byte zz__X_____
        .byte zz____X___
        .byte zz__X_X___
        .byte zz___XX___
        .byte zzX___XX__
        .byte zzX__XXX__
        .byte zz__XXXX__
        .byte zz___XXX__
        .byte zz_XXXXXX_
        .byte zz__XXXX_X
        .byte zz_XXX_XX_
        .byte zz__X_XX__
        .byte zz_XXXXX__
        .byte zz_X_XX___
        .byte zzX_XX____
        .byte zz__X_____
        .byte zz_X______
        .byte zz________
        .byte zz________
        .byte zz________
        .byte zz________
        .byte zz________
        .byte zz________
        .byte zz________     

Broom: ; @$f = 15
        .byte zz_X_XXX__
        .byte zz_XXXXX__
        .byte zz_XXXXX__
        .byte zz_XXXXX__
        .byte zz________
        .byte zz_XXXXX__
        .byte zz__XXX___
        .byte zz___X____
        .byte zz___X____
        .byte zz___X____
        .byte zz___X____
        .byte zz___X____
        .byte zz___X____
        .byte zz___X____
        .byte zz___X____
        .byte zz___X____
        .byte zz___X____        

ExitDownArrow: ; @$19 = 25
        .byte zz___X____ ; 0
        .byte zz___X____ ; 1
        .byte zz___X____ ; 2
        .byte zz___X____ ; 3
        .byte zz___X____ ; 4
        .byte zz___X____ ; 5
        .byte zz___X____ ; 6
        .byte zz_X_X_X__ ; 7
        .byte zz_XXXXX__ ; 8
        .byte zz_XXXXX__ ; 9
        .byte zz__XXX___ ; 10
        .byte zz__XXX___ ; 11
        .byte zz__XXX___ ; 12
        .byte zz___X____ ; 13
        .byte zz___X____ ; 14
        .byte zz___X____ ; 15
        .byte zz________ ; 16

becomes this in Stay Frosty 2:

Broom: ; @$f = 15
        .byte zz_X_XXX__
        .byte zz_XXXXX__
        .byte zz_XXXXX__
        .byte zz_XXXXX__
        .byte zz________
        .byte zz_XXXXX__
        .byte zz__XXX___
        .byte zz___X____
        .byte zz___X____
        .byte zz___X____
;        .byte zz___X____
;        .byte zz___X____
;        .byte zz___X____
;        .byte zz___X____
;        .byte zz___X____
;        .byte zz___X____
;        .byte zz___X____        
ExitDownArrow: ; @$19 = 25
        .byte zz___X____ ; 0
        .byte zz___X____ ; 1
        .byte zz___X____ ; 2
        .byte zz___X____ ; 3
        .byte zz___X____ ; 4
        .byte zz___X____ ; 5
        .byte zz___X____ ; 6
        .byte zz_X_X_X__ ; 7
        .byte zz_XXXXX__ ; 8
        .byte zz_XXXXX__ ; 9
        .byte zz__XXX___ ; 10
        .byte zz__XXX___ ; 11
        .byte zz__XXX___ ; 12
        .byte zz___X____ ; 13
        .byte zz___X____ ; 14
;        .byte zz___X____ ; 15
;        .byte zz________ ; 16        
BirdG: ; @$28 = 40
        .byte zz___X____
        .byte zz________
        .byte zz_XX_X___
        .byte zz__X_____
        .byte zz____X___
        .byte zz__X_X___
        .byte zz___XX___
        .byte zzX___XX__
        .byte zzX__XXX__
        .byte zz__XXXX__
        .byte zz___XXX__
        .byte zz_XXXXXX_
        .byte zz__XXXX_X
        .byte zz_XXX_XX_
        .byte zz__X_XX__
        .byte zz_XXXXX__
        .byte zz_X_XX___
        .byte zzX_XX____
        .byte zz__X_____
        .byte zz_X______
;        .byte zz________
;        .byte zz________
;        .byte zz________
;        .byte zz________
;        .byte zz________
;        .byte zz________
;        .byte zz________     
Link to comment

Hmm... Timmy is a pointer to an array of constant unsigned chars, but Timmy isn't constant.

 

I think what you need to do is declare const unsigned char const Timmy[] = { ... }; Then the second cast (unsigned short int)Timmy should work.

  • Like 1
Link to comment

No luck, thanks for the suggestion!

 

const unsigned char const Timmy[] =
{
    0b00111100,
    0b01011010,
    0b00100100,
    0b00011000,
    0b01111110,
    0b10111101,
    0b10111101,
    0b00111100,
    0b00100100,
    0b00100100
};

const unsigned char const RedBox[] =
{
    0b11111111,
    0b10000011,
    0b10111101,
    0b10111101,
    0b10000011,
    0b10110111,
    0b10111011,
    0b10111101,
    0b11111111
};

const unsigned short int Images[] =
{
  (unsigned short int)Timmy,
  (unsigned short int)RedBox
};

results in:

game.c:41:3: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
   (unsigned short int)Timmy,
   ^
game.c:41:3: error: initializer element is not constant
game.c:41:3: note: (near initialization for ‘Images[0]’)
game.c:42:3: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
   (unsigned short int)RedBox
   ^
game.c:42:3: error: initializer element is not constant
game.c:42:3: note: (near initialization for ‘Images[1]’)

Also tried a number of variations with const in the Images[]:

const unsigned short int const Images[] =
{
  (const unsigned short int const)Timmy,
  (const unsigned short int const)RedBox
};
Link to comment

The best way that I know to do this is the following:

 

1) Use D00D to optimize the data - assume the output is the following:

 

sprite1:
  .byte 1, 2, 3, 4, 5
sprite2:
  .byte 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
sprite3:
sprite4:
  .byte 1, 2, 3, 4, 5, 6, 7
2) Create a C structure to hold the data - unfortunately you do need to specify the length of each chunk of data (you could probaby write a utility to do this):

 

typedef struct {
  const unsigned char sprite1[5];
  const unsigned char sprite2[10];
  const unsigned char sprite3[0];
  const unsigned char sprite4[7];
} SPRITEDATA;
3) Initialize the structure with the sprite data (again this formatting could be done via a utility):

 

__attribute__((used))
__attribute__((packed))
const SPRITEDATA sprites = {
  .sprite1 = {1, 2, 3, 4, 5},
  .sprite2 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
  .sprite4 = {1, 2, 3, 4, 5, 6, 7}
};
4) Now, you can reference the sprite data easily, e.g:

 

// Access the data directly
unsigned char x = sprites.sprite1[6];

// Access the data as an array (C doesn't do bounds checking, so this is actually accessing data in sprite2)
const unsigned char *s1 = sprites.sprite1;
unsigned char x = s1[6];

// Copy the sprite data to a queue
myMemcpy(_QUEUE, (unsigned char*)s1, 16);

// For bonus points you can also access the data as an offset from the beginning of the struct (GCC only)
#define offsetof(type, member)  __builtin_offsetof (type, member)
unsigned char *spritedata = (unsigned char *)&sprites;
unsigned char y = spritedata[offsetof(SPRITEDATA, sprite4) + 3];

// The offsets can also be held in a short array to save memory (over pointers):
const unsigned short offsets[] = 
{offsetof(SPRITEDATA, sprite1),
 offsetof(SPRITEDATA, sprite2),
 offsetof(SPRITEDATA, sprite3),
 offsetof(SPRITEDATA, sprite4)};
Link to comment

To use the approach that I am proposing, the workflow would be something like:

  • Create a file containing the sprite data in the usual DASM format
  • Run D00D over the data to optimize the layout
  • Run a utility to convert the D00D output into C struct format (this would need to be written)
  • #include the C struct containing the sprite data in your code
Chris
Link to comment

Thanks! I'm almost to the point were I'll need sprite data for testing, but don't think I'll get there until next month due to other commitments. When I do I think I'll try the various methods and see how they work out.

 

post-3056-0-09300800-1539473936.png

Link to comment
Guest
Add a comment...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...