Kong [1.0.3]

Size.

There is no need to use both - Playtune can do everything Tones can do, and more. It seems like the only reason Tones is more commonly used is because the format for a Playtune score array is more convoluted than the format for a Tones sequence? (and because Playtune was written to use Timer1, so you can’t use PWM on the RGB LED (for blue and red) at the same time)

ArduboyPlaytune could be modified to use hardcoded pin numbers and direct pin manipulation. This would reduce code size. However, as it is the library is not Arduboy specific, so can easily be used with different pins on other hardware.

I wrote ArduboyTones to provide something that produced less code and possibly smaller scores, when you only want to play simple beeps and/or monotonic sequences.

Later, I added the BeepPinX classes to the Arduboy2 library for something even smaller when all you want is single tones.

I am determined to free up enough memory to use ArduboyPlaytune.

Thanks @MLXXXp … I haven’t had a chance to look at the code and I am not sure I would know what to change. If you can point me to potential change, I will give it a go!

You’ll need an understanding of how to control the pins directly by writing to the port registers. ArduboyPlaytune already directly manipulates the registers (rather than using the Arduino pin functions) but it uses variables rather than constants when doing so. This is where some code could be saved (although I can’t say how much. It may not be very much).

Also, if you’re going to hard code the pins, you can save more code by hard coding the timers as well. (Again, I can’t say how much.)

I would look at the ArduboyTones library as an example of directly controlling specific pins.

If you’re going to do this, I suggest that you copy the ArduboyPlaytune source and make a custom version as a local library that’s part of your sketch, with renamed files and classes. (Be sure to follow the MIT licence it uses.)

Thanks for the feedback, I will probably look at it tonight.

Wrote a sketch that uses ArduboyBeep to read and play the multi-channel patterns in their native format without conversion:

Compiles to the same ~8500 bytes that Playtune did - but this is doing more than just one note for 1/2 second… and storing the patterns in their native format should be a lot more efficient than the Playtune score format?

Also, I edited the pattern to have the duration before every note pair - so this should be even smaller once keeping-the-previous-duration-for-all-notes-until-a-new-duration-is-found gets written in…

2 Likes

Fantastic! Now I have even more incentive to save that memory :slight_smile:

3 Likes

I have added a link to the current ‘Beta’ version.

Current Issues:

  • Occasionally, barrels are launched too close together.
  • There is no theme music (if anyone wants to contribute?)

Recently Fixed:

  • Cable from level 2 appearing in level 1.
  • Made jump a little shorter.
  • I have also added a little ‘Lives Left’ indicator on the scoreboard which scrolls with the game. Not sure if I like it, what do you think?
2 Likes

@Pharap Do you know when constructors and destructors are called implicitly? Kong is spending over 0.5kb just on free and malloc.

It depends on where the variable is allocated.

For statically allocated variables (i.e. globals) the constructor is called once before main and the destructor is called once after main.

For local variables the constructor is called at the point the variable is defined and the destructor is called when the variable falls out of scope and its lifetime ends.

For dynamically allocated variables the constructor is called when new is used and the destructor is called when delete is used.

Why is it using malloc and free in the first place? It probably shouldn’t be.

It doesn’t call free or malloc (that I see), but it does declare destructors on objects:

    virtual ~GameState(void) {};

That’s what that is, right? Could that be pulling them in just as a matter of course? Or are these things entirely unrelated and I’m getting confused?

00001528 00000261 t Images::HighscoreText_Mask
00017874 00000262 T TitleScreenState::update(GameStateMachine<GameContext, GameStateType>&)
00023230 00000262 T Game::loop() [clone .constprop.38]
00001789 00000263 t Images::HighscoreText
00030922 00000274 T free
00030618 00000304 T malloc
00017052 00000344 t ArduboyTonesExt::nextTone()
00016674 00000348 T PlayGameState::activate(GameStateMachine<GameContext, GameStateType>&)
00002052 00000360 t Images::HighscorePanel_Mask
00002412 00000362 t Images::HighscorePanel
00012956 00000426 t Coordinates::Barrel_Splash
00014394 00000448 T HighScoreState::update(GameStateMachine<GameContext, GameStateType>&)
00011265 00000452 t Images::Plate
00003543 00000542 t Images::Gorilla
00013814 00000580 T HighScoreState::render(GameStateMachine<GameContext, GameStateType>&)
00016072 00000590 T PlayGameState::render(GameStateMachine<GameContext, GameStateType>&)
00005611 00000590 t Images::Title_Kong
00000210 00000660 t Coordinates::Barrel
00025032 00000706 T PlayGameState::drawScenery(GameStateMachine<GameContext, GameStateType>&, unsigned char) [clone .constprop.105]
00027224 00000966 t Sprites::drawBitmap(int, int, unsigned char const*, unsigned char const*, unsigned char, unsigned char, unsigned char)
08389252 00001024 B Arduboy2Base::sBuffer
00006262 00001585 T _ZN11CoordinatesL6PlayerE.lto_priv.132
00018460 00002532 T PlayGameState::update(GameStateMachine<GameContext, GameStateType>&)
00008548 00002592 t Images::Crane

Lots of space lost to Images::Crane and those HUGE Coordinates tables.

All the *State classes reference free (via delete):

000044dc <HighScoreState::~HighScoreState()>:
    44dc:	0c 94 f7 37 	jmp	0x6fee	; 0x6fee <operator delete(void*)>


000044e2 <PlayGameState::~PlayGameState()>:
    44e2:	0c 94 f7 37 	jmp	0x6fee	; 0x6fee <operator delete(void*)>


000044e8 <SplashScreenState::~SplashScreenState()>:
    44e8:	0c 94 f7 37 	jmp	0x6fee	; 0x6fee <operator delete(void*)>


000044ee <TitleScreenState::~TitleScreenState()>:
    44ee:	0c 94 f7 37 	jmp	0x6fee	; 0x6fee <operator delete(void*)>

I don’t see where malloc is called anywhere, but maybe it comes along for the ride with free? Not sure why compiler couldn’t strip it though.

I wouldn’t say lost as the coordinates tables are central to the game. Attempting the movements other ways would result in significant code or other reference data. But yes they are HUGE.

The crane images could well be cut down a bit. I have used frames (hence the images are all the same size) and a generic scenery rendering routine (which handles the vertical scrolling as well) which would need to be substituted for code.

Removing the “destructor” mentions from both classes removes free/malloc from the compiled output.

Does the order of const uint8_t PROGMEM Scenery[] = { matter?

Generally no but there are some things, like the cables for the hard level, that I want rendered after other items. These are probably the only things I can think of so they would need to be at the bottom.

Why?

And frees up 660 bytes.

virtual destructors have nothing to do with malloc or free.

virtual destructors are what enable the destructor of a child object to be called when a pointer to said object has been upcast to the object’s parent (or one of the object’s parents).

E.g.

class Base { virtual ~Base() { std::count << "Base" << '\n'; } };
class Child { ~Child() { std::count << "Child" << '\n'; } };

// Don't try this at home, it calls the destructor twice
void function()
{
  Child child;
  // Implict upcast:
  Base * base = &child;

  // Manually call the destructor,
  // which would call both the base destructor
  // and the child destructor
  base->~Base();
  
  // child.~Child(); is implicitly called here,
  // which is a bad thing
}

I’m resisting the urge to post a Trump meme.

Ok, I’ll rephrase my earlier question:
Why is it using new and delete in the first place? It probably shouldn’t be.

For reference:
new is almost always implemented by calling malloc and delete is almost always implemented by calling free.
They’re specifically defined to make that possible.

It doesn’t have to happen that way, the standard explicitly classes ‘the heap’ and ‘the free store’ as separate entities, but I’ve yet to see an implementation where they are actually different.

That sounds like a compiler bug to me.

I can only think of one possible reason why they might be using new and delete.
It’s possible that they’re using it to somehow allocate a buffer to copy the vtable to.
But that seems absolutely ridiculous, and is frankly concerning.
It would explain a lot though.

Make sure the destructors are actually safe to remove.
If they’re all empty then destruction is trivial and it should be fine to remove them and in fact never even call them (because the compiler won’t either - empty/trivial destructors are a nop).

‘Matter’ how? Syntactically?


@Dreamer3 & @filmote
You know you can edit your old comments instead of posting new ones in quick succession, right?

1 Like

Yes.  

1 Like