Could Arduboy FX contain bigger games?

Hello,

A few months ago, I started a very promising project but I was dreaming too big. I ran out of memory before I could make the game playable. I tried to separate the game in parts, using #ifdef; you have to recompile after finishing tutorial for exemple, but it’s kind of messy.

Could the Arduboy FX manage bigger game? This would solve everything.
If not, at least I could have all the parts of my game already installed on the Arduboy.

Without using flash expansion the Cathy3k bootloader alone will hand you some extra space.

There are a lot of tricks you can do to get memory back from using a standard Arduboy. How much were you short do you think?

Do you use the print() functions at all? If not, just changing the code to use Arduboy2Base will save a heap.

1 Like

@Keyboard_Camper Does Cathy3K replace the original bootloader to gain space? But then how do the compiler know there is more space left?

@filmote I have no idea how much space I need. The more I could get the happier I’d be :wink:
I do use the print function, at least in “tuto mode”. I thought about creating sprites instead of words in the menu, not sure if it is optimal.

Thank you for your answers but it doesn’t answer the original question. Is it a no then?

My understanding is you can load memory from the FX flash but I have no idea how to do it. I am really keen to understand this myself!

You have to flash your bootloader with a variant of @Mr.Blinky’s Cathy bootloader and use a custom board package to compile larger games.

I’m not sure if the current version of Cathy is as efficient since it became involved with the FX, it could now be a little more bloated than older iterations due to adding flash features.

Getting an extra ~1k of bootloader space is inconsequential to accessing the ~16MB of potential data.

To be honest, all the code examples are there to do it, it’s a little tricky to understand… but not really.

Ladies and gentleman, I figured it out and I can barely code myself out of a wet paper for loop. Working on uploading the videos but they are a couple gigs and I only have 1meg upload right now.

Seriously, its not that hard to do but you have to show some initiative on it.

1 Like

Ultimately it all depends on what your game actually is.

As @filmote implies there might be ways to fit it on the Arduboy without needing the FX chip, or resorting to anything drastic.

That said, there are ways and means of using the FX chip to store data, but so far there haven’t been many experiments with it.

But even until the FX chip launches there may be ways to split it across multiple .hex files without too much effort, either by using #if or having separate games.

It was supposed to be an exploration game. You start as a simple space miner but as you gain money and upgrade your ship, you can explore more distant places, find some weird aliens and other stuff…
There is still a lot of work to do to make it a complete game but I’m already at the memory limit. Haven’t programmed anything since june.

Here is the link if you’re interested: https://github.com/skaterced/Spaceboy-Adru
(remove #define STORY_MODE_TUTO in globals.h to discover more)

I can’t see any obvious large savings yet, but I can see at least half a dozen potential small savings.
E.g.

  • You’re missing a lot of F macros
  • Using more variables in functions like Station::draw might help
    • Despite what a lot of people believe, reusing the same variable is actually worse for performance than using lots of variables because of the way the compiler assigns CPU registers to variables
  • There’s a fair number of warnings reported by the compiler that might save memory if fixed
    • Especially the functions with missing returns
  • Splitting some of your longer functions up into smaller functions might help.
  • Doing some manual variable caching might help

Most of all, I think you need to try to make your code more data driven.
E.g.
In your shop code all the items modify money and setup,
and all the conditions are testing agaist money and setup, so if you could find a way to encode the other changes, you could store all the items in an array and test that way.
E.g. something like

struct ShopItem
{
  uint8_t money;
  uint8_t setupBit;
};

constexpr ShopItem shopItems[]
{
  { 40, GUN_BIGB },
  { 60, GUN_AUTO },
  { 80, GUN_MULTI },
};

And then

ShopItem item = shopItems[selector];
if((ship.money > item.money) && ((ship.setup & item.setupBit) == 0))
{
  ship.money -= item.money;
  ship.setup |= item.setupBit.
}

Then on Ship instead of having multi you could have

bool isMulti() const
{
  return ((this->setup & GUN_MULTI) != 0);
}

and instead of canHold you could have

bool canHold() const
{
  return ((this->setup & GUN_AUTO) != 0);
}

Anyway, you get the idea - relying on interpeting data instead of explicitly writing lines of code for every change, especially when there’s a pattern.

Another example, instead of having a long chain of arduboy.println you could use the flash string trick to do something like:

const char line0[] PROGMEM = "Sorry, only available";
const char line1[] PROGMEM = "in Race mode";
const char line2[] PROGMEM = "";
const char line3[] PROGMEM = "Recompile whith";
const char line4[] PROGMEM = "#define RACE_MODE ";
const char line5[] PROGMEM = "in globals.h";

const char * const lines[] PROGMEM
{
	line0,
	line1,
	line2,
	line3,
	line4,
	line5,
};

void writeLines()
{
	// An alternative to hardcoding 6 is to use the 'getSize' trick:
	// https://github.com/Pharap/SizeDemo
	for(uint8_t index = 0; index < 6; ++index)
		arduboy.println(readFlashStringPointer(&lines[index]));
}

Which should be cheaper (in terms of progmem) because it’s a single function call in a loop instead of multiple consecutive function calls.


Unfortunately there will be some limitations on what you can manage because of the variety of things that you have occurring in the game, so you probably won’t manage all of what you intend to without the FX chip, but there’s quite a few things you can do to cut the memory use down.

@Pharap Thank you for taking the time to read my code and to give me advices. I appreciate your help.
I will try to implement them and see how much memory they save.

1 Like

ç’est n’ai pas une problème.


Two last things…

Remove volatile, that disables optimisations and you don’t need it unless you’re writing interrupt handlers.

And this:

Is a good candidate for the ‘data driven’ approach.
Store the offsets in an array of int8_t (preferably in progmem) and then have a loop instead of lots of drawLine calls.

If you aren’t sure how to implement any of the suggestions I made, feel free to ask for advice, explanations or assistance.

Yes. You can offload graphics and game data to FX.

It doesn’t solve everything. You can not store and execute program code from the FX
chip. If your code doesn’t fit into ATmega32u4 you could’ll create some sort of interpreter or virtual machine that runs your byte code from FX

it is treated as a form of virtual memory. There are functions that draw a bitmap directly from flash and you can read data by passing along a virtual memory address (offset)
There will be a tool that parseses a script and creates a blob file that will be uploaded to FX flash and a C++ header file containing all the virtuall offsets to the blob data. (still WIP)

I think it’s best to understand from examples. If you want I could give you targeted help for adaptring Loderunner for FX

It still is 3K :slight_smile:

2 Likes

Had a go on you game and looks very nice.

You can save quite a few bytes by reducing texts by using a single println() instead.

For Example using a single println() like this saves 52 bytes :

      ab.println(
        "\n" 
        "Written by\n" 
        "   C" "\x82" "dric Martin\n" 
        "\n" 
        "  Mai 2020\n"
        "\n" 
        "\n" 
        "A or B: back");

The above string is still stored in RAM but by adding the F() around the string you free valuable ram (56 bytes) :

      ab.println(F(
        "\n" 
        "Written by\n" 
        "   C" "\x82" "dric Martin\n" 
        "\n" 
        "  Mai 2020\n"
        "\n" 
        "\n" 
        "A or B: back"));
1 Like

That most likely blows my array idea out of the water.

Is it not possible to just load executable memory data into ram as a string or something and then jump to it as an execute? I think you’d have to do that with in line assembly to actually get it to compile? Basically, an “code injection” attack? Kinda like how you can program mario to do stuff by loading up the buffer in exactly the right way then executing an overflow?

The ATmega32U4 (and AVR in general) uses a (modified*) Harvard Architecture. Program code can’t run from RAM.

* Modified by the fact that read only data can be stored and retrieved from the program (flash) area.

1 Like

So jump commands can only be issued to valid flash memory addresses?

Correct. Program memory and data memory are on separate buses. The are program addresses 0x0000, 0x0001, 0x0002… and data addresses 0x0000, 0x0001, 0x0002…

In fact, program is addressed as 16 bit words and data is addressed as 8 bit bytes.

It’s kind of like saying: With a PC you can’t jump from RAM to an address on a hard drive and start executing code directly on the hard drive.

2 Likes

:grin: Thank you

Ok so if I understand correclty: with the FX, I could store sprites or string arrays but not code right?

1 Like