A couple of generic questions about arduboy development (loop function, up/down fx reset, EEPROM, Emulator etc)

Hi,

The game I’m working on is a port of a Gameboy game i once made, I’m currently testing it all but the game seems to function fine as far as i can tell but i do have a couple of questions.

  1. Is it bad to never exit the main loop function ? My game has game states and i just have a big game state loop inside the main loop function where each game state calls another function with a loop specific to that game state and remains in that loop until the game state changes. In each of those function i make use of arduboy.NextFrame as usual but use a continue statement instead of exit. This seems to work all fine, but I’m just wondering if never exiting the main loop function might have some kind of consequences. And i rather not change this if it’s all fine todo this as It would require a lot of changes in my game.

  2. About the UP / DOWN key combination for resetting on arduboy fx, i found a thread that links to a custom wiring.c that handles this but i have not included it in my game. Should i release my game without this and rely on python tools or the FX Builder site to patch my game or is it better to include this beforehand? If so what happens on older arduboy’s without fx chip ?

  3. I’m using “normal” libs and not the homebrew optimized arduboy board specifications with optimized core code (i only found out about it later). My game runs fine with the normal board specifications (about 3-4 % CPU usage at 10 fps) and arduboy libs is there any reason why i should be using the homebrew / optimized core instead of all the default libs and should i switch to using the optimized core / homebrew code ?

  4. About EEPROM usage, I’m guessing on the arduboy FX games can have collisions on saving to EEPROM (like using the same addresses), is there a list somewhere which games use which addresses or did the FX games got patched somehow to prevent least amount of collisions or should i report somewhere (upon release) which EEPROM addresses my game uses ?

  5. EDIT: i did exactly this, disable sound when both sound and music are turned off, and also disable sound / music on boot if the global sound flag is set to disabled. I enable global sound again if either music or sound is being enabled in options screen. So this question is void as well
    Original question:
    I read somewhere there is a reserved EEPROM address to enable or disable sound globally, my original game had options to disable / enable sound and music separately and i still use this so i also save these 2 separate options to EEPROM. I’m guessing i should somehow enable / disable this global sound flag as well (for example if both sound and music is turned off) otherwise if another game disabled sound completely it won’t be possible to enable sound (globally) again in my game.

  6. EDIT: This is not true anymore, i was using an older version of project ABE (v078) and there this was needed but in project ABE (v081) this is not needed anymore the audio flag seems to be set to “1” by default so calling arduboy.audio.on() is not needed anymore
    original question:
    Is there any way to know if I’m running inside the project ABE emulator or on the real device ? To make (partial) sound work in the emulator i have to explicitly call arduboy.audio.on() because EEPROM is completely initialized with zero’s as far as I’m aware but on the real device i should not call this and make use of the global sound setting to adhere to people having sound turned off. So i need a way to know if I’m running on the real device or in the emulator to call that function or not. How do people handle this ? And is there a way to know this ?

1 Like

List is here. There’s also a recent ‘meta’ summary of all historic posts.

It sounds like you’re duplicating effort here. Check out the standard Arduboy2 library features for managing sound, here.

Great question. That would be a really useful feature, as other games have also had to be adapted for issues with the emulator. I can imagine a byte in the system reserved section of the EEPROM, could be initialised with a secret code to indicate the emulator is in use. As you know, this is similar to how GB games detect the hardware revision. Currently EEPROM bytes: 0x000 and 0x0030x007 are available. Perhaps a byte could be set to ‘e’ (=0x65)? Also in Byte 0x002, the system bits 3–7 are unused (but this feels too small). Alternatively, the ‘Unit ID’ at 0x0080x009 might be used (I’m not sure it’s used elsewhere[1])? @MLXXXp @FManga ?

[1] Original design intent for ‘Unit ID’ is here.

Hi thanks for the answers …

I’m aware of and using those function but my game has a distinction between music and sfx sound and has options to turn either on or off individually. What i do now is i set my internal music & sound enabled flag equal to value of my saved state music & sound flag “anded” with arduboy.audio.enabled so that when people have disabled audio globally no music and no sound will be played even if my saved state has those options set to 1. When people goto my options screen in such a state and explictly enable sound (or music) again i call arduboy.audio.on() as well as arduboy.audio.saveOnOff and when they turn off both sound and music on the option screen i call arduboy.audio.off() as well as arduboy.audio.saveOnOff this seems to work fine. The reason i have an internal sound & music flag is because i had this from the gameboy code but also because i temporary disable music / sound at certain spots in the game and it should not change the global sound enabled flag in such cases.

Another thing i like about using this setup is that when people have turned of global sound but had for example only sound and no music enabled in my saved state, no sound or music will play but if they enable global sound again (externally using B button or set by another game) my game would only play sound again and no music because it used the saved state flags again. So it remembered the users previous options before having disabled the global sound flag (externally)

I am however not sure if games should call arduboy.audio.saveOnOff like are the “enable sound” options in a game a reflection of the global sound enabled flag and does it switch this flag (thus have to call arduboy.audio.saveOnOff or should we never touch the global sound enabled flag and only consider it a temporary change inside the game? The last thing might confuse people if they had enabled sound in my game options and restart the game but see the sound being disabled again because the global sound flag was still set to disabled… so i’m guessing in most games “Enable sound” options are a reflection of the global sound enabled flag and when it gets its value changed we should also save it to EEPROM using arduboy.audio.saveOnOff

I haven’t added that feature because it causes more problems than it solves:

Imagine the getEmulatorVersion() function returns a positive number or 0 if it’s on hardware.

Say, theoretically, a bug in the latest version of the emulator causes sound to crash.
A game that detects that it’s running in the emulator (version > 0) could then disable sound.
When the next version of the emulator comes out, the game would still not have sound even if the bug was fixed.
If the game tried to be more specific (version > 0 && version < 7) there could still be a problem if the bug wasn’t fixed.

Another problem I’ve seen is when there’s a bug in the game and for some reason it only manifests itself in the emulator. The author wanted emulator detection to tell players to use real hardware, but the proper fix was to initialize their variables instead of relying on whatever noise was in RAM.

1 Like

Thanks for the quick reply- your answer makes a lot of sense :slight_smile:
I wasn’t sure if the emulator was still under active development and was thinking of the recent issue of a missing opcode.
I use your wonderful emulator a lot… probably more often than my ‘real’ hardware Arduboys. My old laptop struggles with some of the graphically intensive games, and cannot maintain the frame rate. Dropping some features for smoother emulation made sense to me. E.g. sound has never really worked and isn’t desirable in the office! :wink:

1 Like

If you mean the void loop() function, then yes.

There’s actually a main function hidden in the Arduino library that does some initialisation stuff, then calls setup, and then calls loop from within a loop. That loop also deals with the serialEventRun function.

I’m not sure how much of that other stuff is strictly necessary on Arduboy, I think you could probably get away with not having it, but it’s probably not a good idea to bypass it unless you really need to.

This is generally a bad idea because you’ll end up duplicating code that could be shared by all the states.

For example. you’ll always want to call arduboy.nextFrame for every state because that’s what enforces the frame rate, so it’s better to do it once at the top than to keep calling it from different sub-loops, otherwise that call is going to be duplicated multiple times, which will waste progmem.

I’m sceptical that it would be as much work as you think.

Unless you’re doing extra work before the loops, or giving the loops some sort of loop condition, it should just be a matter of replacing the continues with returns, getting rid of the loop construct, and moving the arduboy.nextFrame, arduboy.pollButtons and arduboy.display calls into loop. (And anywhere you have a break would be replaced with changing the state and then doing a return.)


There’s a reset button. The Arduboy FX units also have a reset button.
Turning the Arduboy off and on again also does mostly the same thing (on non-FX units at least), which is typically the better way because the reset button is a bit more fiddly and fragile.

The up+down combination is mainly useful for FX units that have been set to automatically boot into the last played game, or as a simple convenience.

The only reason to worry about any of this is that sometimes games that use a lot of memory end up in a position where the device won’t recognise a request to upload a new game.

The vast majority of the time simply turning the Arduboy off and then booting it into ‘flashlight’ mode by holding the up button when turning it on is enough to solve the issue.


It’s up to you.
Personally I don’t think it’s worth it unless you really need the extra memory.

Ultimately if your game is open source then users can choose whether they compile with the Arduboy2 library or the homemade variant.

You could always release both if you really wanted.

(In the past I used to provide .hex files for the people who still used the old Arduboy devkits, though I don’t think the demand is there anymore.)


Just to check, you should be using the official functions for dealing with whether the global ‘mute’ bit is set.

https://mlxxxp.github.io/documents/Arduino/libraries/Arduboy2/Doxygen/html/classArduboy2Audio.html

In particular, you should call begin (as either arduboy.audio.begin() or simply Arduboy2Audio::begin()) to automatically have the sound enable or disable itself based on the EEPROM mute bit, and you should call enabled to test whether global mute was enabled or disabled.

(Note that off, on and toggle don’t affect the value saved in the EEPROM settings, you’d have to call saveOnOff to overwrite the value in EEPROM.)

After that you can deal with sound or music being locally muted using your own setup, and save it along with your save date (i.e. not touching the first 16 bytes of EEPROM that store the library’s global save settings).

“Do whatever works” is my motto!

Hi thanks, i adapted the code to fix this as per your suggestions, but i had to adapt a bit as i indeed did extra things prior to calling the loops (init function), but i just check now if the previous game state was different than the gamestate function that is being called and if so i call the init function normally it should be called only once per gamestate function entered as long as the gamestate does not change

Do you mean if my game is running it should react to uploading new games ? It seems this does not work on my arduboy FX when my game is running, i always seem to have to return to the FX main menu (game selection menu screen) to be able to upload a new version of my game. I have not tested if it works if i run another game

Ah ok, i thought it was some default to easily return to game selection menu on the fx. I’ll skip including wiring.c then and rely on external tools to patch my game to add this functionality as it seems non fx arduboys don’t really require this

Yes the game is open source so i think i’ll stick to the default libs as i don’t seem to have any problems related to that nor do if have any problems with progmem (or did you mean ram usage)

yes i use those functions … when i talk about saving / changing global mute setting i mean by calling on or off´ and then calling saveOnOff at certain conditions for example when sound was turned off by global mute flag and people turn sound (back) on in my game i call on and then saveOnOff otherwise if they restart my game the sound won’t be enabled even though they selected sound to be on in a previous play of the game.

It’s still not very clear to me if we may call for example on and then saveOnOff if global mute flag was set to false when people choose to enable sound in my game.

I think i’ll adapt based on comments once i release the source as it will be more clear what i mean and what i do

Thanks again for all your (and other people’s) answers it has helped me already

1 Like

Another way to handle that is to have game states dedicated to initialising other game states. Then you can switch to the initialised state at the end of the initialisation code and let the initialised state start on the next frame.

Alternatively, if you really want to avoid wasting a whole frame on initialisation code, then you can either let the initialising case fall through or use a goto to jump to the next case, to make the transition more explicit. If you rely on fallthrough, it’s best to use a fallthrough attribute - the compiler possibly won’t recognise the attribute because the compiler targets C++11 and the fallthrough attribute is from C++17, but it shouldn’t cause an error and it’s a good way to let readers know that the fallthrough is intentional.

If you upload via the USB cable, yes, the Arduboy should detect the serial communication and respond to upload commands.

Sometimes if a game uses a lot of memory this system stops working.

Does it work if you hold up when booting the game (i.e. to activate ‘flashlight’ mode)?

If so, your game probably does something that upsets the USB connection, in which case there’s not much you can do other than trying to reduce your memory usage. It happens sometimes with larger games.

If the ‘flashlight’ mode trick still works then you don’t need to worry.

I could be wrong, but I think the FX units might have the up+down combination implemented via the bootloader. (My FX unit is a prototype with an older bootloader, so it doesn’t behave quite the same as an official FX unit would.)

I think the homemade package only saves progmem, but it might save a small amount of RAM too.

Either way, it’s better if your game works with both rather than requiring the homemade package.

This is true, but just for the record: it’s also possible to enable and disable the global sound bit by holding the B button on boot and pressing up to enable and down to disable.

Ah it never crossed my mind todo this it might indeed be a better / cleaner way i’ll test it later thanks again for the pointers.

i would need to check ram usage (not sure it’s possible to know how much ram is currently in use), as on the gameboy it was better to have most variables as global variables, so i still need to clean up that bit but currently i have this related to progmem & ram (i already use arduboy2base class as i don’t use print call)

Sketch uses 25526 bytes (89%) of program storage space. Maximum is 28672 bytes. Global variables use 1458 bytes (56%) of dynamic memory, leaving 1102 bytes for local variables. Maximum is 2560 bytes.

I don’t need todo this, all i need todo is turn off / turn on the arduboy fx so it sits at the that screen where you can press A or B to boot the last started game (or press left / right to browse through other games) and then it will upload new games without a problem. After work i’ll try upload my game while other games are running to see if it works there. I was under the impression i always had to restart the arduboy fx to be at this “bootloader” menu to be able to upload a new game from arduino ide or vs code (+ plugin) i actually was not aware this might be faulty and should also work while games are running.

yes i already found out about this and it’s how i tested global mute with my game.

1 Like

I figured out the problem, it does work when my (& other games) are running however windows assigns a different COM port when the game is running. In bootloader mode it uses COM3 when a game is running COM4 no idea as to why the devices do get disconnected & reconnected when a game starts. If i switch to COM4 uploading still works. I’m not sure i can force windows to always use the same COM port when in bootloader mode or running a game will need to test. But seems there was no issue at all it just failed because it used the wrong COM port

Edit: managed to assign the same COM port to the bootloader connection & connection when game is running. Only one is active at a time so i guess it’s safe todo so. You can change this in device manager on the driver properties tab of the COM ports in advanced settings

1 Like

Are you sure about that? That’s the opposite of how things usually are.

Usually it’s better for variables to be local, unless they’re significantly large or exist for the whole lifetime of the program. In particular, variables should generally be as local as they possibly can be. E.g. you wouldn’t want a loop counter to be a global.

On AVR (the CPU architecture the Arduboy uses) avoiding globals is generally good anyway because accessing globals typically requires more instructions.

(Though constants are a different matter.)

As long as you’re not intending to add too many more features, that’ll be fine.

Around 90% is a large game but still resonably safe.
95% is more concerning.
98-99% is when you should really start worrying about cutting progmem usage wherever possible.

I don’t know what kind of game you’re making, but that’s less RAM than I was expecting.
(Unless you’re using new or malloc, in which case that number won’t be accurate. new and malloc should be avoided.)

I tend to consider 2048 the point at which RAM is getting dangerously full because it only leaves 512 bytes for the stack.

No, but testing a ‘normal’ USB upload would tell you if there are likely to be any issues for non-FX units


I was about to suggest checking that the COM port is correct had I not spotted your second comment.

It explains the issue and means neither your game nor your device have any problems.

I’m not entirely sure why it happens when a game starts, but the upload process involves switching COM port anyway, so it’s probably not unusual.

I wasn’t actually expecting that to work, so it’s interesting to know that it does.

However, I’ve found that sometimes the COM port just randomly changes after a while for no apparent reason. It’s probably related to how windows assigns port numbers and/or recognises devices. So be aware that there’s a chance that the COM port might randomly change anyway, so you might have to change it back again at a later date.

1 Like

as far as i remember that is what i’ve been told. It was something related to putting & getting things from the stack was really slow or produced slow code but it may have been specific to the GBDK-2020 (C) SDK i was using it used SDCC in the background. Here’s the quote from their Docs:

Global and local static variables are generally more efficient than local non-static variables (which go on the stack and are slower and can result in slower code).

I’m aware it usually is the other way around

Thanks for this info, i know why my progmem might be big though, i have a few tilesheet images from the gameboy version i quickly “imported” as a C constant but i did not bother to optimize these, i just exported all the tiles as it was more convenient than trying to change everything completely again so that code worked, i might try to optimize it again later but i’m not running into space issues so i might leave it as is. I also have 3-4 songs in a const arrray in progmem they are not that big but it does add to the number.

I’m not using malloc or new at all. The game basically uses just a few tiles that i keep reusing.

The game is puzzle game it’s called Waternet and it is my own implementation of 2 games i once saw on the simon tatham puzzle collection (it uses the “rules” from the net and netslide game but it’s my own implementation).

here is a gif i just “recorded” from arduboy version in project ABE “aim” is to fill all water pipes (fully white) starting from the water source in the middle by either sliding (cols / rows) or rotating the tiles depending on the game mode

waternet

Thanks again for taking time to answer my questions you have helped a lot already.

3 Likes

Really excited to play this! :smiley:

2 Likes

Why is 95% or more concerning / not safe? Many games use 99.9% of the progmem and have no problems. The only risk is a change to the Arduboy libraries might cause a future compile to exceed the 100% but that’s a future problem or somebody else’s problem.

This is really healthy. Even the basic ‘Hello World’ uses 1240 bytes!

i’m still optimizing i’m currently at the following

Sketch uses 23056 bytes (80%) of program storage space. Maximum is 28672 bytes. Global variables use 1392 bytes (54%) of dynamic memory, leaving 1168 bytes for local variables. Maximum is 2560 bytes.

be aware though i’m using arduboy2base class and not arduboy2 but not sure it impacts ram much (i don’t use the print functions, i had created my own fonts on the gameboy version and i’m reusing those) I’m guessing the print functions in hello world might take some memory ?

I also RLE Compressed 2 bitmaps (which are not used as tiles) using team arg’s cabi program. I did
notice drawCompressed is fairly expensive compared to uncompressed but in my case it’s still fine i only use it to draw a titlescreen (when needed) and a fullscreen bitmap (once)

I have used the compressed bitmaps but found it really only made sense when all my images where compressed - otherwise you have both libraries loaded. On some ocassions, I sliced images up so that I could use the standard Sprites library but this only works if you have large areas of a solid colour.

Also, if you do not need the speed you can swap to SpritesB library. It is slightly slower but I have never noticed a real difference in speed and it will save a few hundred bytes.

I suspect so. Anyhow, the default font is huge in size and by using your own font, you can use a smaller font on screen.

There are a number of smaller fonts including the 5x3 and 4x6 that I created. Here is a comparison to the standard font.

image

Font 3x5
Font 4x6

1 Like

Not sure i understand all i use is arduboy2 library drawBitmap and drawCompressed i’m not using a sprite class (the game is too simple for it and i really only had 4 sprites on the gameboy to draw the cursor (on arduboy it’s 2) but i implemented a simple workaround for it

I use my tiled image for example like so :

const unsigned char *currentTiles = NULL;

void set_bkg_tile_xy(uint8_t x, uint8_t y, uint8_t tile)
{
  arduboy.drawBitmap(x * 8, y * 8, &currentTiles[2 + (tile * 8)] , 8, 8);
}

void set_bkg_data(const unsigned char *tiles)
{
  currentTiles = tiles;
}

where set_bkg_data takes the uncompressed bitmap progmem constant array as parameter (my tiles are 8x8). The weird names are like that because that is how the functions were more or less called with the gameboy SDK so i kept them so i did not need to replace all those function calls

Is this not valid perhaps ? I’m not really a c/c++ developper so don’t know all the intricates of it i’m usually happy when i can get things running fairly ok without crashes, my background is delphi / object pascall i only ever used C/C++ for creating SDL(2) games and the gameboy game

If it works, then it works!

There are methods for rendering in the Arduboy2 library (which you are using) and an alternate set in a class called Sprites. I prefer the Sprites library myself as it handles masks better.

https://mlxxxp.github.io/documents/Arduino/libraries/Arduboy2/Doxygen/html/classSprites.html

2 Likes

‘Safe’ might be a poor choice of word.

Basically you have to pick some point at which you start saying “this is getting close to the limit, I’d better start trying to conserve memory”, and to me 95% seems a good point to do that. You could wait until the last minute, when things are 99% and you’re really up against it, but personally I prefer to have that 5% ‘wiggle room’ in case things start getting bigger before they get smaller (because that helps when checking to make sure the behaviour hasn’t changed).

They don’t cause problems for the device or for the user, but they cause you (the developer) a problem because you have to start ejecting and rewriting things to regain some memory.


Odd.

I’m presuming they mean the overhead of actually setting up the variables and saving old variables when doing a function call slows things down, which is technically true, but it seems excessive to ward people off doing that.

The thing is, a good compiler will try to put local variables in registers whenever possible, and will only keep them on the stack if: they need too be saved before or during a function call, there are too many live variables to be able to keep them all in registers, the variables are being passed as arguments to a function and the calling convention requires them to be placed on the stack, or the address of a variable is taken (because registers don’t have addresses). In contrast, a global variable must be kept in RAM and certain optimisatons (e.g. eliminating a redundant write) often can’t be performed on a global.

Anyway, I’m rambling. The bottom line is that the opposite is usually true, and it’s particularly true for AVR (i.e. Arduboy’s CPU architecture). (See page 5 of this document for more details.)

Like I say, unless you have plans to add more features then you’ll be fine with how things currently are.

That’s good. I didn’t think you would be using them, but I thought I’d check anyway because people get so used to using new and/or malloc on desktop that they tend not to think about how they actually work and/or don’t realise they’d cause issues on a system with only 2.5KB of RAM.

(Also when someone comes from a Java or C# background into C++ it tends to take them a while to find out that new isn’t used in quite the same way and is generally avoided in favour of other options.)

A very handy page, I’ll be bookmarking that.

(That ‘Map’ puzzle is obviously inspired by the four colour theorem.)

Looks quite good. I can see that’s not the standard font.

No problem. If you have any other questions, feel free to ask.

80% is well under the limit. At this point any memory you save is pretty much an academic excercise (again, unless you have plans to add any more features or ‘polish’).

It wouldn’t be a massive saving, but it would still save a few bytes.
(At least 11, going by a quick peek at the source.)

They would, but they’d have more of an impact on progmem than RAM.

@filmote sort of beat me to the punch, but most of us use the functions provided by the Sprites class because there are more options.

(‘Sprites’ is a bit of a misnomer really since it’s a collection of drawing functions rather than proper ‘sprite’ objects in the traditional sense.)

No, it’s perfeclty valid, as long as currentTiles is pointed at data in progmem and not data in RAM.
(Though nullptr is preferred over NULL as of C++11.)

Also, I don’t think drawBitmap does a null check, so you might want to either add an if(currentTiles != nullptr) before the drawBitmap call as a precaution or make sure currentTiles is initialised to a valid image in the first place.

It’s quite unusual to find people using Pascal these days, but personally I have a bit of a soft spot for it.