This is probably the longest reply I’ve written in quite some time…
If you bought an Arduboy FX with the pre-installed FX mod it’ll have a different bootloader.
If you bought the official FX mod and installed it yourself then chances are you also used the on-board control chip to overwrite your Arduboy’s existing bootloader.
You’ll know the difference because it’s the custom bootloader that allows you to load games off the FX chip, so if you had the standard Arduboy bootloader you wouldn’t be able to load FX games.
Either way that’s not going to have any effect on the compiler because the compiler only uses the settings you give it.
That’ll probably be explain the 200 byte difference. It’s probably from the customised ‘wiring’ library.
Are you sure it’s set to ‘standard Arduboy’ though?
If it is, I would have expected it to still be reporting “Maximum is 28672 bytes.” because the ‘standard’ Arduboy has a 4KB bootloader.
The fact your compiler reports “Maximum is 29696 bytes.” implies that it’s being told that the bootloader is only 3KB, which means it’s probably being told you’re using the ‘Cathy3K’ bootloader somehow.
It’s not automatically an issue, and probably won’t be an issue for you because you probably do have the 3KB bootloader, but when your programs are large enough to get near the limit, just remember that people who still have the standard bootloader don’t have that extra 1KB, so if you start eating into that limit you might want to make a note somewhere that your game isn’t compatible with the standard bootloader.
The 1KB difference hasn’t actually been an issue so far because in the past only a few people have overwritten their bootloaders, but with the FX units coming along I expect it’s only a matter of time before we start getting games that rely on the extra 1KB and it becomes more of an issue.
Unfortunately it’s one of the many things Arduino did in an attempt to be more ‘beginner friendly’, and it’s far from the worst thing they’ve done in that respect.
Unfortunately saving memory isn’t just a “do these N things and your program will definitely be smaller” situation. Saving memory is a very situational affair that depends heavily on what your code is doing and whether the compiler’s got there first or not.
There are several dozen different things that usually save memory, but many are situational, they aren’t guaranteed to always save memory and some can even do the opposite in some situations for some reason.
I can’t list everything, but I’ll mention a few simple things:
- Obviously using
F
as much as possible is one of those things.
- There have been occassions where not using
F
for some calls (but still using F
for others) has actually worked out better for reasons unknown.
- Instead of having lots of branches that each call the same function with different parameters, it’s often cheaper to make the branches set variables and then call the function at the end.
- This is because function calls are expensive on AVR whilst variable assignments are cheap, so having one call and multiple assignments is often cheaper than having lots of calls.
- If you know how function calls actually work ‘under the hood’ then this should make even more sense.
- If a function is being called with the same parameters from multiple branches, it’s worth checking to see if it’s actually always being called, and if so it’s typically cheaper to move it outside the branch (because then you aren’t duplicating the calling code).
- Sometimes the compiler is smart enough to do this for you, other times it won’t, but either way it makes your code cleaner and easier to read when you make it clear that a function is always called.
- Using lots of smaller functions instead of a handful of larger functions typically saves memory.
- The short version of why this is the case is that the compiler finds it easier to optimise smaller blocks of code because it has less to keep track of. The issue isn’t so much the amount of code, but the complexity of ensuring that certain properties continue to hold as the scope increases in size.
One memory-saving suggestion specific to your code:
Instead of maintaining clockwise
and anticlockwise
as separate variables, if you stored difference
as a global you could replace all uses of if(clockwise)
with if(difference < 0)
and all uses of if(anticlockwise)
with if(difference > 0)
. If you still want decent readability, you could introduce functions that do the same job.
E.g.
bool wasTurnedClockwise()
{
return (difference < 0);
}
bool wasTurnedAnticlockwise()
{
return (difference > 0);
}
This wouldn’t be a massive memory saving, but it’s little savings like this that eventually add up. Any kind of redundancy that can be eliminated is a bonus.
One other thing I noticed while looking at your code again which might be a bug...
It seems to me that most (if not all) of those button#
variables in gamecontroller
probably aren’t doing what you think they’re doing. It seems to me like you’re assuming they’ll keep their value when the function is called again, but they won’t because they’re local variables and local variables are destroyed when a function ends.
If on the other hand they actually are doing what you want them to, then it seems to me that you don’t actually need them because (for example) button2 != pipboy.pressed(UP_BUTTON)
will always be equivalent to false != pipboy.pressed(UP_BUTTON)
which is effectively the same as just pipboy.pressed(UP_BUTTON)
. Following that logic, when you do button0 = !button0;
and then Joystick.setButton(0, button0);
it’s equivalent to Joystick.setButton(0, true);
. Like I say, if that wasn’t the intent then you need to make those variables global.
Before I explain ‘F-wrapped strings’, how much do you know about the following?
- Progmem (and reading from it)
- Function overloading
- Type aliases
const
(The more you know about these, the less I’ll have to explain. If you don’t know any of these, I’ll have to explain each of them first.)