Jump to content
RevEng

Squeezing the bBytes

Recommended Posts

There comes a time in the development of most games when the rom starts to get tight; when no additional features can be added until you figure out a way to squeeze the bytes and free up more space.

 

I haven't seen this information gathered in one place before, so I thought I'd kick off this thread to share some of the techniques I've used to save rom space. So with no further adieu, here they are...

 

 

  • keep same-value variable assignments on the same line when possible.
     

     rem ***** do this...
    
       timer=0:level=0:deathflag=0:soundindex=0
    
     rem ***** instead of this...
    
       timer=0
       level=0
       deathflag=0
       soundindex=0
    


     
    Since they're on the same line, bB knows it doesn't need to keep loading the 0 value for each assignment.
     
    The savings is fairly minimal (2 bytes for each variable after the first one) but this is an easy technique to use and the savings can add up quickly!
     
    (NOTE: the current version of bB seems to max out at 12 assignments per line. If you use more than that you may run into compile errors. :( )

 

 

[*]when using bankswitching, try to reduce the number of bankswitching gosubs and gotos.

 


  • A regular gosub takes 3 bytes. A bankswitching gosub takes 24 bytes.
    A regular goto takes 3 bytes. A bankswitching goto takes 18 bytes.
     
    You should avoid having small utility routines that are bankswitch-gosub'ed from many places. Either move the utility into the same bank as the calls, or change all the bankswitching gosubs to a local gosub that has a common bankswitching goto routine...
     
rem ***** do this...

  if lives=1 then gosub utilityjump
  health=1
  gosub utilityjump
  goto mainloop
utilityjump
  goto utility bank3

rem ***** instead of this...

  if lives=1 then gosub utility bank3
  health=1
  gosub utility bank3
  goto mainloop


 
If there's a bank with a lot of utility functions, you can save rom by using on...goto and a temp variable...
 

rem ***** use this...

  if lives=3 then temp1=0:gosub enterbank2
  if enemies=0 then temp1=1:gosub enterbank2
  if gold=1 then temp1=2:gosub enterbank2
  goto mainloop

enterbank2
  goto dispatcher2 bank2

  bank 2
dispatcher2
  on temp1 goto routine1 routine2 routine3
routine1
  rem do stuff
  return otherbank
routine2
  rem do stuff
  return otherbank
routine3
  rem do stuff
  return otherbank

rem ***** instead of this...

  if lives=3 then temp1=0:gosub routine1 bank2
  if enemies=0 then temp1=1:gosub routine2 bank2
  if gold=1 then temp1=2:gosub routine3 bank2
  goto mainloop

  bank 2
routine1
  rem do stuff
  return otherbank
routine2
  rem do stuff
  return otherbank
routine3
  rem do stuff
  return otherbank


 

 

[*]In a bank-switched game, if you're sure a return is always going to be within the same bank, use "return thisbank" instead of "return". If the return will always be to another bank, use "return otherbank" instead of "return".

 


  • In a bank-switched game...
     
    A "return otherbank" statement takes 3 bytes.
    A "return" statement takes 13 bytes.
    A "return thisbank" statement takes 1 byte.
     
    'nuff said.

 

 

[*]If you have a large amount of data with values less than 16, Use nibbles and hex notation to stick the data together

 


rem ***** use this...
  channel=songvolumechannel[i]&15
  volume=songvolumechannel[i]/16
  data songvolumechannel
  $86, $48, $84, $86
end

rem ***** instead of this...
  channel=songchannel[i]
  volume=songvolume[i]
  data songchannel
  6, 8, 4, 6
end
  data songvolume
  8, 4, 8, 8
end


 

 

[*]pick variable locations consecutively so you can operate on many objects using a loop

 


  • If you're doing a lot of the same operation to a bunch of variables, often it's better to pick the position of variables carefully and use loops...
     
rem ***** use this...
  dim monster1x=g
  dim monster2x=h
  dim monster3x=i
  dim monster4x=j
  for temp1=0 to 3
  if monster1x[temp1]>25 then monster1x[temp1]=1
  if monster1x[temp1]<0 then monster1x[temp1]=24
  next

rem ***** instead of this...
  if monster1x>25 then monster1x=1
  if monster1x<0 then monster1x=24
  if monster2x>25 then monster2x=1
  if monster2x<0 then monster2x=24
  if monster3x>25 then monster3x=1
  if monster3x<0 then monster3x=24


 

 

 

I've also been able to squeeze a fair bit of ROM through algorithmic improvement and judicious assembly code rewrites, but those techniques aren't really suited to a simple tips thread. I figured I'd still mention it, so you can keep them in mind.

 

Please post any techniques you've discovered!

  • Like 8

Share this post


Link to post
Share on other sites
If you're doing a lot of the same operation to a bunch of variables, often it's better to pick the position of variables carefully and use loops...

 

rem ***** use this...
  dim monster1x=g
  dim monster2x=h
  dim monster3x=i
  dim monster4x=j
  for temp1=0 to 3
  if monster1x[temp1]>25 then monster1x=1
  if monster1x[temp1]<0 then monster1x=24
  next

rem ***** instead of this...
  if monster1x>25 then monster1x=1
  if monster1x<0 then monster1x=24
  if monster2x>25 then monster2x=1
  if monster2x<0 then monster2x=24
  if monster3x>25 then monster3x=1
  if monster3x<0 then monster3x=24

I don't understand the use of monster1x[temp1]. I thought square brackets were only used when reading data. Can you explain more about this other use of square brackets?

 

Thanks.

Share this post


Link to post
Share on other sites
I don't understand the use of monster1x[temp1]. I thought square brackets were only used when reading data. Can you explain more about this other use of square brackets?

 

Thanks.

Your little gray cells just need a jump start. :)

 

It's how you access RAM arrays which are read-write. (scroll down to the RAM array info, near the end of that section)

Share this post


Link to post
Share on other sites
Your little gray cells just need a jump start. :)

 

It's how you access RAM arrays which are read-write. (scroll down to the RAM array info, near the end of that section)

Thanks. So to be clear and without using dim for my simple examples here, a[0] is the variable a, a[1] is the variable b, a[2] is the variable c, and so on. Is that correct?

Share this post


Link to post
Share on other sites

Thanks. So to be clear and without using dim for my simple examples here, a[0] is the variable a, a[1] is the variable b, a[2] is the variable c, and so on. Is that correct?

Completely correct

Share this post


Link to post
Share on other sites

Thanks. So to be clear and without using dim for my simple examples here, a[0] is the variable a, a[1] is the variable b, a[2] is the variable c, and so on. Is that correct?

Completely correct

Thanks.

Share this post


Link to post
Share on other sites
  • keep same-value variable assignments on the same line when possible.
     
     rem ***** do this...
    
       timer=0:level=0:deathflag=0:soundindex=0
    
     rem ***** instead of this...
    
       timer=0
       level=0
       deathflag=0
       soundindex=0
    


     
    Since they're on the same line, bB knows it doesn't need to keep loading the 0 value for each assignment.
     
    The savings is fairly minimal (2 bytes for each variable after the first one) but this is an easy technique to use and the savings can add up quickly!

 

I was wondering . . . if you are clearing/setting up variables that aren't using the same value, is it better to have them on separate lines by themselves?

Share this post


Link to post
Share on other sites

I was wondering . . . if you are clearing/setting up variables that aren't using the same value, is it better to have them on separate lines by themselves?

When they have different values, putting the assignments on the same line produces identical assembly to putting the assignments on separate lines.

 

If the variables are related I like to put them on the same line to make the source code easier to read.

Share this post


Link to post
Share on other sites

I was wondering . . . if you are clearing/setting up variables that aren't using the same value, is it better to have them on separate lines by themselves?

When they have different values, putting the assignments on the same line produces identical assembly to putting the assignments on separate lines.

 

If the variables are related I like to put them on the same line to make the source code easier to read.

OK, thanks.

Share this post


Link to post
Share on other sites

Fantastic write up, one of the largest problems I ran into with my original game I was programming was I was constantly running out of room, and now I know why, I was doing many things incorrectly that were taking up WAY to many bytes.

 

Can we make this a sticky? I think this is that important. Also RevEng and Random Terrain, I didn't know we could have arrays in batari Basic! Is that a recent thing? I figure Random probably has a write up on arrays on his site, so I'll have to go check it out later today.

 

-Disjaukifa

Edited by disjaukifa

Share this post


Link to post
Share on other sites
. . . I didn't know we could have arrays in batari Basic! Is that a recent thing? I figure Random probably has a write up on arrays on his site, so I'll have to go check it out later today.

Most of the text on the bB page was written by batari. I think this has been on the bB page for a long time:

 

http://www.randomterrain.com/atari-2600-memories-batari-basic-commands.html#dataarrays

Share this post


Link to post
Share on other sites
. . . I didn't know we could have arrays in batari Basic! Is that a recent thing? I figure Random probably has a write up on arrays on his site, so I'll have to go check it out later today.

Most of the text on the bB page was written by batari. I think this has been on the bB page for a long time:

 

http://www.randomterrain.com/atari-2600-memories-batari-basic-commands.html#dataarrays

 

Ok I remember reading about that now, I didn't use them because I couldn't modify the content in the arrays easily. I know you can with the code from SeaGruff found here: Read/Write Arrays

 

-Disjaukifa

Share this post


Link to post
Share on other sites
keep same-value variable assignments on the same line when possible.

While experimenting, have you noticed how many variables can be on a single line before bB chokes on it (with and without bankswitching)? If you have found a general limit, have you also noticed if the length of variable aliases changes things (does size matter :D )?

Share this post


Link to post
Share on other sites
So to be clear and without using dim for my simple examples here, a[0] is the variable a, a[1] is the variable b, a[2] is the variable c, and so on. Is that correct?

Here's what I'm using in Seaweed Assault at the beginning of the program:

 

 

  rem  ****************************************************************
  rem  *
  rem  *  Clears all normal variables and old playfield variables.
  rem  *
  rem  '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  rem  '
  for temp5 = 0 to 25 : a[temp5] = 0 : next

  for temp5 = 0 to 47 : var0[temp5] = 0 : next

 

I'm using a modified version inside of the game that avoids clearing out variables that need to keep their values.

 

 

Thanks again for the idea.

Share this post


Link to post
Share on other sites
While experimenting, have you noticed how many variables can be on a single line before bB chokes on it (with and without bankswitching)? If you have found a general limit, have you also noticed if the length of variable aliases changes things (does size matter :D )?

 

It appears that you can only have 12 assignments on one line. Any more and there's a segfault error.

 

I think it's due to some of the hardcoded memory limits in the 2600basic binary. (from statements.c)

 

I'll add that as a note in my first post.

 

And your looping workaround is a good one! :thumbsup:

  • Like 1

Share this post


Link to post
Share on other sites
It appears that you can only have 12 assignments on one line. Any more and there's a segfault error.

 

I think it's due to some of the hardcoded memory limits in the 2600basic binary. (from statements.c)

 

I'll add that as a note in my first post.

 

And your looping workaround is a good one! :thumbsup:

Thanks. :thumbsup:

Share this post


Link to post
Share on other sites

I wanted to add onto this a little bit. I just went through a game I'm working on, and was able to save about 60 bytes so far where I was calling rand or setting up values that I would then do again later in the main loop before that sprite was ever used. I have a feeling many of use could save space as well by going through and removing needless code.

 

You also need to find a balance between using goto statements and if statements. Depending on what you are evaluating one can save you more space than the other. Mostly you want to stick to if statements from my observation except when you get into a serious of very long if statements that has multiple && checks in them.

Share this post


Link to post
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.

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...