cmacrossavegameboygame-boy-advance

Trouble defining macros in C - Initializer element is not constant


I'm programming for the Game Boy Advance and I need to have a list of memory locations for each area(a bunch of RAM and ROM).

However, while defining the macros in a header file, the compiler pointed out that on one macro, error: initializer element is not constant.

enter image description here

Here is my full header file(approx. 90 lines) that I borrowed from Tonc:

#ifndef TOOLBOX_H
#define TOOLBOX_H

// === (from tonc_types.h) ============================================

typedef unsigned char   u8;
typedef unsigned short  u16;
typedef unsigned int    u32;

typedef u16 COLOR;

#define INLINE static inline

// === (from tonc_memmap.h) ===========================================

#define MEM_IO      0x04000000
#define MEM_VRAM    0x06000000
#define GAMEPAK_RAM 0x0E000000

#define REG_DISPCNT     *((volatile u32*)(MEM_IO+0x0000))
#define REG_VCOUNT      *(volatile u16*)(MEM_IO+0x0006) // vertical count

// === (from tonc_memdef.h) ===========================================

// --- REG_DISPCNT defines ---
#define DCNT_MODE0      0x0000
#define DCNT_MODE1      0x0001
#define DCNT_MODE2      0x0002
#define DCNT_MODE3      0x0003
#define DCNT_MODE4      0x0004
#define DCNT_MODE5      0x0005
// layers
#define DCNT_BG0        0x0100
#define DCNT_BG1        0x0200
#define DCNT_BG2        0x0400
#define DCNT_BG3        0x0800
#define DCNT_OBJ        0x1000

#define save_mem        ((u8*)GAMEPAK_RAM) //<- Error here

// === (from tonc_video.h) ============================================

#define SCREEN_WIDTH   240
#define SCREEN_HEIGHT  160

#define vid_mem        ((u16*)MEM_VRAM) //But not here

INLINE void m3_plot(int x, int y, COLOR clr)
{   vid_mem[y*SCREEN_WIDTH+x]= clr;    }

#define CLR_BLACK   0x0000
#define CLR_RED     0x001F
#define CLR_LIME    0x03E0
#define CLR_YELLOW  0x03FF
#define CLR_BLUE    0x7C00
#define CLR_MAG     0x7C1F
#define CLR_CYAN    0x7FE0
#define CLR_WHITE   0x7FFF

INLINE int CLAMP(int val, int min, int max)
{
    if(val >= max)
    {
        val = max - 1;
    }
    else if(val < min)
    {
        val = min;
    }
    else
    {
        return val;
    }
    return val;
}

INLINE COLOR RGB15(u32 red, u32 green, u32 blue)
{   return red | (green<<5) | (blue<<10);   }

INLINE u16 * get_RGB(COLOR clr)
{
    u16 red, green, blue;
    red = clr & 31;
    green = (clr >> 5) & 31;
    blue = clr >> 10;
    static u16 rgb[3];
    rgb[0] = red; rgb[1] = green; rgb[2] = blue;
    return rgb;
}

INLINE void vid_vsync()
{
    while(REG_VCOUNT >= 160);   // wait till VDraw
    while(REG_VCOUNT < 160);    // wait till VBlank
}

#endif // TOOLBOX_H

However, I did not have these two lines in my code originally:

#define GAMEPAK_RAM 0x0E000000

and

#define save_mem ((u8*)GAMEPAK_RAM)

If you noticed, I purposely defined save_mem almost exactly like vid_mem except that I needed to cast it to u8 because that RAM could only read/write 8 bits at a time.

Then I thought that if there were errors, then both lines should have errors. However, only the save_mem got caught with error: initializer element is not constant. (I personally commented out the save_mem line to see if vid_mem would have that problem.)

Also, I was already in the middle of programming my game for the GBA and I had already used vid_mem many times, so this puzzles me even more.

I am wondering why this is occurring and how to fix it.(Pretty sure my compiler is fine)

Thanks in advance.

EDIT:

Here is the code where I use the save_mem macro:

#include "toolbox.h"

u8 played = save_mem[0];
u8 coins = save_mem[1] | (save_mem[2] << 8) | (save_mem[3] << 16) | 
(save_mem[4] < 24);

void get_coins()
{
    coins = save_mem[1] | (save_mem[2] << 8) | (save_mem[3] << 16) | 
(save_mem[4] < 24);
}

void save_coins(int amount)
{
    save_mem[1] = (amount & 255);
    save_mem[2] = ((amount >> 8) & 255);
    save_mem[3] = ((amount >> 16) & 255);
    save_mem[4] = ((amount >> 24) & 255);
}

void set_played()
{
    save_mem[0] = 1;
}

Solution

  • I could only reproduce your problem by doing

    u8 played = save_mem[0];
    

    outside of any function. This is not an error in your define, but an error in the above line. The way the error was presented is really misleading. Anyway, the error happens because the line above will require the program to load contents from memory, and as you expect, there is no way for the compiler to know what will be in this memory at compile time, and global variable initializations only accept constants. You should move this line of code inside a function.

    The code I used to test:

    #ifndef TOOLBOX_H
    #define TOOLBOX_H
    
    // === (from tonc_types.h) ============================================
    
    typedef unsigned char   u8;
    typedef unsigned short  u16;
    typedef unsigned int    u32;
    
    typedef u16 COLOR;
    
    #define INLINE static inline
    
    // === (from tonc_memmap.h) ===========================================
    
    #define MEM_IO      0x04000000
    #define MEM_VRAM    0x06000000
    #define GAMEPAK_RAM 0x0E000000
    
    #define REG_DISPCNT     *((volatile u32*)(MEM_IO+0x0000))
    #define REG_VCOUNT      *(volatile u16*)(MEM_IO+0x0006) // vertical count
    
    // === (from tonc_memdef.h) ===========================================
    
    // --- REG_DISPCNT defines ---
    #define DCNT_MODE0      0x0000
    #define DCNT_MODE1      0x0001
    #define DCNT_MODE2      0x0002
    #define DCNT_MODE3      0x0003
    #define DCNT_MODE4      0x0004
    #define DCNT_MODE5      0x0005
    // layers
    #define DCNT_BG0        0x0100
    #define DCNT_BG1        0x0200
    #define DCNT_BG2        0x0400
    #define DCNT_BG3        0x0800
    #define DCNT_OBJ        0x1000
    
    #define save_mem        ((u8*)GAMEPAK_RAM) //<- Error here
    
    // === (from tonc_video.h) ============================================
    
    #define SCREEN_WIDTH   240
    #define SCREEN_HEIGHT  160
    
    #define vid_mem        ((u16*)MEM_VRAM) //But not here
    
    INLINE void m3_plot(int x, int y, COLOR clr)
    {   vid_mem[y*SCREEN_WIDTH+x]= clr;    }
    
    #define CLR_BLACK   0x0000
    #define CLR_RED     0x001F
    #define CLR_LIME    0x03E0
    #define CLR_YELLOW  0x03FF
    #define CLR_BLUE    0x7C00
    #define CLR_MAG     0x7C1F
    #define CLR_CYAN    0x7FE0
    #define CLR_WHITE   0x7FFF
    
    INLINE int CLAMP(int val, int min, int max)
    {
        if(val >= max)
        {
            val = max - 1;
        }
        else if(val < min)
        {
            val = min;
        }
        else
        {
            return val;
        }
        return val;
    }
    
    INLINE COLOR RGB15(u32 red, u32 green, u32 blue)
    {   return red | (green<<5) | (blue<<10);   }
    
    INLINE u16 * get_RGB(COLOR clr)
    {
        u16 red, green, blue;
        red = clr & 31;
        green = (clr >> 5) & 31;
        blue = clr >> 10;
        static u16 rgb[3];
        rgb[0] = red; rgb[1] = green; rgb[2] = blue;
        return rgb;
    }
    
    INLINE void vid_vsync()
    {
        while(REG_VCOUNT >= 160);   // wait till VDraw
        while(REG_VCOUNT < 160);    // wait till VBlank
    }
    
    #endif // TOOLBOX_H
    
    /*void bob(){*/
        u8 played = save_mem[0];
    /*}*/
    

    To compile I copied from your screenshot:

    arm-none-eabi-gcc -mthumb-interwork  -mthumb -O2 test.c -c
    

    but added -c to avoid link(since there is no main function).