Gracefully handle the magic number

I’ve got tons of flash left in my game but large ram usage has meant my game now needs flashlight mode to reboot?

Is there a better way to handle this now that I know my game will do this?

Is there a way to actually capture the reset from within my game and manually put the magic number for the bootloader so it will go there?

You can put your game into a state that will do the same as flashlight mode.

power_timer0_disable();
while (true) { }
1 Like

But there is no way to grab the serial reset or anything?

Or put your game in this mode for a certain amount of time?

Could I…

power_timer0_disable();
for(int i=0; i<42069;i++){ }
power_timer0_enable();

No I guess not, this sucks.

Sure, but if the reset occurs outside that window, the magic number could still end up being clobbered.

I’m not sure about detecting the interrupt. It may be possible but I haven’t looked into it.
You might be able to check if the magic number has been set before starting each frame render and if it has then do:

power_timer0_disable();
while (true) { }

It may be that the best you can do is add a menu item for “upload mode” that would put the game into the “safe” state above.

Otherwise, you could just document the fact that this game may require flashlight mode. Since it’s more or less unpredictable which games will require it, it’s pretty much an Arduboy fact of life. We should resign to striving to educate everyone about it.

Having to use flashlight mode is really not much different than having to hold Down for games that have the USB code removed.

And if you wanted, you could start shipping Arduboys with a modified bootloader that fixes the problem. You could also offer this bootloader for people to install on existing Arduboys if they have the equipment and skills to do it.

You could reduce your memory consumption,
or reduce the depth of your function calls,
though that’s not always practical.

It’s a good suggestion, but… there are a lot of particles.

Once I post the game you can help me try to squeeze it if you want to laugh at my garbage coding skills. I’m certain there is a lot of room for improvement. I’m actually pretty pleased with how fast this game came together, it only took me 3 days!

This works thanks @MLXXXp

uint16_t bootKey = 0x7777;
volatile uint16_t *const bootKeyPtr = (volatile uint16_t *)0x0800;

void getBooted(){
  uint16_t bootKeyPtrVal = *bootKeyPtr;
  if(bootKeyPtrVal == bootKey){
    power_timer0_disable();
    while (true) { }
  }
} 

void loop() {

  getBooted();
  // game code that uses too much ram goes here
}

Hey @MLXXXp any chance of adding this function to the library as safeMode() or something?

1 Like

Maybe this should just be rolled in to the nextFrame() function automatically? Almost all sketches will use this and call every frame so it would fix ones that use too much RAM.

Eh, it’s really only necessary if you’ve hit the magic number, and it takes up some flash, so it has a penalty. It also doesn’t work all the time.

On top of it, with the FX it doesn’t matter since you’re always reset to the bootloader.

It’s just nice if it was a documented function instead of a code snippet you have to find here.

UPDATE: I suppose depending on your games proclivity to write the number 30583 into memory, you could accidentally trigger this too.

Can someone explain why using too much RAM causes this situation and what the magic number is all about? I just sort of accepted it as part of developing for Arduboy but like to understand what the issue is

3 Likes

The PC triggers a reset to bootloader by turning off and on the serial port for a certain amount of time (500ms I think).

This is done by a interrupt routine that is watching the serial port, and what it does is set a value in ram at location 0x0800 to 0x7777 (30583 in decimal). And then resets.

The bootloader checks memory location 0x0800 for 0x7777 (the magic number) and if it is found, then it triggers the upload, otherwise it just loads the program.

So if you have a game that is using location 0x0800 for something else, and changes the value between the time the interrupt is triggered and the reset actually occurs, then the boot loader does not see the value and it won’t upload.

This should have been at the end of memory space… if you had only 2k of memory. Whoever wrote the catarina bootloader wasn’t paying attention (or had ulterior motives) to the fact you actually have 2.5k of memory so this ends up being like 82% (I think) of memory which is when you get this warning from arduino:
image

(It didn’t use to give this warning, this is an admission of the problem)

Actually, arduino even called out the Arduboy in a patch to the software, to move the magic number all the way out to the end of memory, but never actually implemented it in the bootloader. This “New Bootloader” that is mentioned in their blog post is vaporware. (Or we were intended to make it on our own, I assumed they meant the most recent one included in the package at the time, so I used that without reading the code)

Actually, if this is still valid, we could solve the problem for every game except for those using the very last 4 bytes of ram.

@Mr.Blinky can we do this?

UPDATE: I might be understanding how the new code works now that I read it closer, it using lufa somehow, did they roll back this change?

I was busy doing production at the time wasn’t able to fiddle around with code.

2 Likes

Not quite. The bootloader is triggered when DTR is cleared with baudrate configured at 1200 baud. this condition normally happens when you open a com port at 1200 baud and then close it.

Actually it sets the Watchdog timer (WDT) to 120 ms and continues with the program. If the WDT doesn’t get cleared during this time then arduboy resets.

The reason for the 120ms period is prevent accidental resets when a com port is opened as some OS-es may not set DTR instantly and the use of the magic key is to allow using the normal WDT feature.

So during these 120 ms Arduboy gets the chance to corrupt the magic key and when it does the WDT will trigger a reset rather than a bootloader trigger.

The key can be corrupted when there are more than 1792 bytes of ram used or by a large stack caused by using many/large local variables.

No, cause the changes are in the bootloader.

Here’s some alternative code that ensures the magic key remains intact. It tests if the watchdog timer is enabled.

  if (WDTCSR & _BV(WDE)) // if watchdog timer is enabled. disable ints and set magic key
  {
    cli();
    *(volatile uint8_t *)MAGIC_KEY_POS = lowByte(MAGIC_KEY);
    *(volatile uint8_t *)(MAGIC_KEY_POS + 1) = highByte(MAGIC_KEY);
    while(true);
  } 
3 Likes

With the FX chip, isn’t the boot loader being reflashed? Is it possible to fix this there? (Sorry if I’m missing something very obvious!..)

1 Like

Thank you for the detailed description @Mr.Blinky!!

Yeah my suggestion was to fix this for the FX bootloader, although it’s not really needed because it boots into the bootloader by default.

Actually I fixed this very early when I started working on my custom bootloader. But recently I removed the magic key check and a WDT reset will always trigger the bootloader menu. The lufa signature is still present so the USB code will store the magic key at end of ram preventing corrupted variables or stack at 0x800 in case of a false positive.

3 Likes

Well, that’s… smart. And easy, and correct.

Considering we aren’t a heart monitor or anything!