Jump to content
IGNORED

Advanced optimizations in CC65


laoo

Recommended Posts

1 hour ago, laoo said:

Just pointing out that @ilmenit wrote an article about cc65 optimizations in Atari 8-bit subforum. I think it could be of interest regarding Lynx development

 

 

 

Nice article. Many of these details are the ones that I have found out the hard way. Usually working for hours to find a byte I can get rid of.

 

Using global variables may be bad coding technique but it helps keeping the code small.

 

The biggest savings that I have found is to always maximize the segment size. Using small segments and flipping them in and out reduces available RAM. It is better to duplicate graphics in the ROM as there is plenty of space in ROM compared to RAM. Again bad coding but it helps you get the extra mile.

  • Thanks 1
Link to comment
Share on other sites

Something one can deduce from the "struct of array" optimization is to not use struct for single entity.

 

For instance, if you have:

Struct SGameManager {
  int timer;
  uchar state;
  bool isPaused;
  etc...
};
SGameManager myGameManager;

 

Since you're very likely going to have one and only one game manager, then you better define it like that:

int myGameManager_timer;
uchar myGameManager_state;
bool myGameManager_isPaused;

 

it's faster and reduce your code significantly.

Edited by LordKraken
  • Like 2
Link to comment
Share on other sites

6 hours ago, LordKraken said:

Something one can deduce from the "struct of array" optimization is to not use struct for single entity.

 

For instance, if you have:


Struct SGameManager {
  int timer;
  uchar state;
  bool isPaused;
  etc...
};
SGameManager myGameManager;

 

Since you're very likely going to have one and only one game manager, then you better define it like that:


int myGameManager_timer;
uchar myGameManager_state;
bool myGameManager_isPaused;

 

it's faster and reduce your code significantly.

It's not necessary ? If you have the structure like above as a static in global space, then CC65 generates code equal to accessing separate variables as below. I'd recommend to keep the SGameManager for code readability.

Edited by ilmenit
Link to comment
Share on other sites

I found out that compiler does this optimizations looking at e.g. cc65\include\_atarios.h where nested structs and unions are used and finding out that compiler is generating very efficient code for them. Which version of cc65 does not do such optimizations? I could add to the article min. version.

Link to comment
Share on other sites

On 4/25/2020 at 10:16 AM, LordKraken said:

It's newcc65 and, as far as I know, it has very little to do with cc65, (maybe it's a very old fork?), but @sage or @42bs probably know more, if not all, about that :)

 

It has a few cool stuff though, like inline asm.

Acutally, I have no idea where the current cc65 differs from my version.

See

http://www.monlynx.de/lynx/cc65notes.html

for more info about "newcc65"

 

Link to comment
Share on other sites

Very nice article @ilmenit!  That should be very helpful for people who are programming in cc65.  I wasn't sure if you mentioned __fastcall__, but that may be a default behaviour of cc65 now.  I find that if you are used to the way that cc65 does things, you learn to program in the "cc65" way.  Generating assembly listings and map files when you compile is mandatory, and you should get in the habit of looking at them to figure out what cc65 is doing both from a code generation point of view, but also memory usage (in larger projects).  In larger projects I don't see how you can really use static locals (you'll run out of memory), but maybe there would be some cases where that might work.

 

When I was porting Moria to the Atari 8-bit, mostly I started optimizing in this order:

 

1. Making sure all the data types were the smallest possible (unsigned char instead of int).

 

2. Using the register keyword to have some local variables in zeropage (cc65 only has 6 zeropage "register" variables), and there is a little overhead to save and restore the contents.  Usually loop variables and pointers are good candidates.

 

3. Any large local variables, were changed into globals.  Some of those could be shared (if very careful), like buffer string space.

 

4. Moved heavily used globals to zeropage, both to optimize memory and speed.

 

5. Lookup tables, for speed mainly.  

 

6. With Moria, I needed space and would often be trying to eke out bytes to get things to fix in cartridge banks.  Including making sure that read-only data was stored in the cartridge bank and not in the main memory RODATA bank.  Memory management is a huge thing with a small 64k machine.

 

 

Edited by Shawn Jefferson
Link to comment
Share on other sites

7 hours ago, Shawn Jefferson said:

 In larger projects I don't see how you can really use static locals (you'll run out of memory), but maybe there would be some cases where that might work.

I think it may depend on how many variables you are using in functions, but additional stack operations and accesses in the code make the code size so big that at least for me it was less memory consuming to use static-locals.

7 hours ago, Shawn Jefferson said:

2. Using the register keyword to have some local variables in zeropage (cc65 only has 6 zeropage "register" variables), and there is a little overhead to save and restore the contents.  Usually loop variables and pointers are good candidates.

while there are only 6 "register" variables, you can define a lot of them just putting variables into Zero Page segment.

 

All your points are great hints!

 

How is the Moria project going? I think it may really challenging to port big code into banked memory. I wish it could be somehow automated with CC65.

 

I'm a big fan of roguelikes myself and even did a few ? The biggest but never finished was Xenocide, which is still available on http://xeno.chaosforge.org/ and reviewed e.g. here https://weyland.fandom.com/wiki/Xenocide:_The_Roguelike

Link to comment
Share on other sites

  • 2 weeks later...
On 4/30/2020 at 11:23 PM, ilmenit said:

I think it may depend on how many variables you are using in functions, but additional stack operations and accesses in the code make the code size so big that at least for me it was less memory consuming to use static-locals.

 

Your code also becomes non-reentrant, which means that you have to be very careful with leaf functions.  I suppose that would have to be a somewhat complex program though (??), but still, that may be a nasty surprise if you aren't ready for it.  On the Moria port, I had to be careful of that when I was moving local variables from the stack into zeropage.  I believe I did find a few examples where I couldn't move the variables easily off the stack.

 

On 4/30/2020 at 11:23 PM, ilmenit said:

while there are only 6 "register" variables, you can define a lot of them just putting variables into Zero Page segment.

Using the register keyword avoids the problem I mentioned above, but there are only 6 bytes available (I shouldn't have said 6 register variables, since an int will use up two byes.  And the code to push and pull them off the stack that allows your functions to be reentrant.  A good compromise solution for some extra speed without changing the program much.

 

On 4/30/2020 at 11:23 PM, ilmenit said:

All your points are great hints!

 

I thought posting a reply would be good for anyone who's looking at CC65 for programming, and add to the points you made in your document.  I didn't think you'd needed the advice, you've been using the suite for a long time now!

 

On 4/30/2020 at 11:23 PM, ilmenit said:

How is the Moria project going? I think it may really challenging to port big code into banked memory. I wish it could be somehow automated with CC65.

 

I've been away from it for a while now, but I do have a more recent version I should upload that has some bugfixes and more polishing.  There's still a bit left to do, to truly call it done.

 

On 4/30/2020 at 11:23 PM, ilmenit said:

I'm a big fan of roguelikes myself and even did a few ? The biggest but never finished was Xenocide, which is still available on http://xeno.chaosforge.org/ and reviewed e.g. here https://weyland.fandom.com/wiki/Xenocide:_The_Roguelike

They are an interesting game genre, and I played them a lot through high school and at jobs where I had only had a monochrome PC at work. :)

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...