0

I have a struct:

typedef struct Image {
    byte height;
    byte width;
    byte data[];
} Image;

and I create 2 images:

static const __flash Image GRID = {
    .width = 16,
    .height = 8,
    .data = {
        0x10, 0x10, 0x28, 0x28, 0x44, 0x44, 0x82, 0x82, ...
    }    
};

static const __flash Image HOUSE1 = {
    .width = 24,
    .height = 24,
    .data = {
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ...
    }
};

Then I create an array of pointers to the images:

static const __flash Image *IMAGES[] = {
    &GRID,
    &HOUSE1,
};

I can draw an image using my draw_image() function:

void main(void)
{
    draw_image(IMAGES[0], 16, 16);
}

I have a map:

typedef struct Map {
    word cols;
    word rows;
    byte tiles[];
} Map;

static const __flash Map level_1 = {
    .cols = 16,
    .rows = 8,
    .tiles = {
        0,0,1,0,...

the .tiles field is a list of indexes into the IMAGES array. I'm doing it this way because my engine doesn't know what images are available without being told:

void draw_map(const Map __memx *map, const Image __memx *tileset[]);
{
    ...
    draw_image(tileset[map->tiles[index]], x, y);
    ...
}

called thusly:

void main(void)
{
    draw_map(&level_1, &IMAGES[0]);
}

The compiler does not like this and gives me incompatible pointer type warnings. The map is not drawn:

note: expected 
‘const __memx Image ** {aka const __memx struct Image **}’
but argument is of type 
‘const __flash Image ** {aka const __flash struct Image **}’

I did try removing the [] from the draw_map() declaration:

void draw_map(const Map __memx *map, const __memx Image *tileset);

but that gave me an error in calling the draw_image() call:

error: incompatible type for argument 1 of ‘draw_image’
             draw_image(tileset[0], c*8+(64 - r*8), r*8);
                        ^
tile-engine.c:63:6: note: expected 
‘const __memx Image * {aka const __memx struct Image *}’ but argument is of type 
‘Image {aka const __memx struct Image}’

Where am I going wrong?

void draw_image(const Image __memx *image, int x, int y)
{
    byte rows = image->height>>3;
    byte cols = image->width>>3;

    for(byte r=0 ; r<rows ; r++)
    {
        for(byte c=0 ; c<cols ; c++)
        {
            draw_tile(&image->data[(r*cols+c)*8], &image->data[(r*cols+c)*8], x+(c*8), y+(r*8));
        }
    }
}
8
  • What is Map ? Where it comes from ? Commented Feb 3, 2019 at 17:24
  • Why do you have to declare draw_map parameters with __memx ? Commented Feb 3, 2019 at 17:24
  • @Niloct Sorry, the __flash and __memx are for the compiler - it's an AVR program and the flash program storage and SRAM have different address spaces. Commented Feb 3, 2019 at 17:27
  • @bruno sorry, I've added the Map typedef, very similar to Image really Commented Feb 3, 2019 at 17:30
  • Could you provide the definition of draw_image? Commented Feb 3, 2019 at 17:31

1 Answer 1

2

The issue seems to be exactly what the compiler has identified: you are passing a __flash pointer to a function that requires a __memx pointer.

If you change the signature of draw_map to

void draw_map(const Map __memx *map, const Image __flash *tileset[])

then it works fine.

Okay, so why is this necessary, when the compiler is okay with accepting a __flash pointer for the first argument, which is also defined as __memx?

The reason is that the first pointer is passed by value while the second is passed by reference (tileset is a pointer to a __memx pointer).

According to the AVR documentation, __flash pointers are 16-bit pointers into (obviously) flash memory, while __memx pointers are 24-bit pointers that can point to locations either in flash or in static RAM.

It looks like the compiler is smart enough to promote a 16-bit __flash pointer to a 24-bit __memx pointer when passing it by value (similar to how it can promote a 16-bit short to a 32-bit int or long), but it can't cause a 16-bit pointer that's stored in memory (in your IMAGES array) to expand to 24 bits.

Since __memx pointers are slower to use than __flash pointers (apparently the compiler has to check if the actual pointer refers to flash or static RAM and use different instructions for each), if you know your image and map data will always be in flash, just pass __flash pointers.

Sign up to request clarification or add additional context in comments.

1 Comment

I had no idea using __memx had such an impact on performance - I changed all of the uses to __flash and went from ~43 ms/frame to ~30. Still a bit on the slow side sadly, but interesting.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.