Jump to content





Minor C issue, any ideas?

Posted by SpiceWare, in SpiceC 28 September 2018 · 289 views

Finally getting some work done on SpiceC and have run into an issue.  Storing graphic data in the C code like this:
Attached Image
 
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 constant
game.c:42:3: error: initializer element is not constant
 
Anybody have any ideas?  Thanks!






try:
const byte Timmy[] instead of const unsigned char

I used const byte for all of my colecovision project.

  • Report
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!
  • Report

I just checked Coleco.h header and you're correct. 
 

typedef unsigned char byte;
  • Report
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.
  • Report
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________     
  • Report

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.

  • Report
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
};
  • Report
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)};
  • Report
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
  • Report

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

  • Report

Search My Blog

Recent Entries

Recent Comments

Latest Visitors

2 user(s) viewing

0 members, 2 guests, 0 anonymous users