This article was published as:

"Game Size: The Forgotten Discipline"
 PC Techniques, Vol. 6, no 3
 Aug/Sept. 1995, page 93.

What appears here is the original manuscript, as submitted to Jeff
Duntemann. Any changes in the published version are due to Jeff's
expert editing.


                        Writing Smaller Games
                     copyright 1995 Diana Gruber

At a recent conference, I ran into a user of a shareware game who
was complaining that the game wouldn't run on his ancient 80286
computer. It was running too fast. The program, apparently, was
written years earlier to run on an XT, and performed badly on
anything faster. He was wondering if I had any suggestions on how
to fix it. I didn't, but I took the opportunity to recall the good
old days of game programming, when developers knew how to get the
most out of those old, primitive systems.

The thing that sticks in my mind the most about the early days
of game programming was how much attention we paid to the size
of a game. These days, all the emphasis is on speed. If a game
fits on ten floppy disks or a CD-ROM, it isn't a problem. We
can afford to be wasteful where size is concerned, as long as
we have adequate speed.

Do you remember when floppy disks were so expensive that games
had to fit single 360K disk? Disk space wasn't the only problem
back then. You couldn't count on a system having 640K bytes of
memory, or even 512K. To reach the widest audience, a game
had to run in 256K or less.

Programming to those types of minimal configurations is becoming
a lost art. Game programmers who remember how it was done still
have the necessary skills. We remember how to optimize for size.
But you don't see much written on the subject, because it is
no longer an issue like it used to be. Still, there is no reason
to be sloppy. Wasted space is wasted space. Even if you don't
need to optimize for size, it still pays to know how to do it.
And there are still occasions where optimizing for size is
important, for example when writing code for an embedded system
or an EPROM. 

With this in mind, I am offering the following list of suggestions
for optimizing games for size.

Use C, not C++

C++ is a powerful language. While you can use it without a speed
degradation, you will most likely notice an increase in the size
of your program. When optimizing for size, use C, stripped down
to the bare minimum. Also, compare compilers. Some compilers
generate smaller code than others.

Check your compiler switches

Most compilers give you a choice between optimizing for speed and
optimizing for size. Choose optimizing for size, and be sure to
turn off the debugging information. Keep an eye on things like
stack size and heap size, and adjust as appropriate.

Be careful with overlays

The big problem with overlays is the number of disk accesses they can
generate. If these are not planned carefully, your program will
access the hard disk constantly, causing your program to run very
slowly. However, if used correctly, overlays can be a wonderful way
to keep a game running when RAM is low.

Use the medium memory model if possible

You want your data to default to near memory. You can fit a lot
of data in a 64K segment, especially if you make heavy use of globals
and reuseable arrays. If Windows Write can be written in medium
model, so can your game. The small model is also good, but it is usually
not practical to fit both code and data into 64K segments.

Use the smallest integral type

Don't use long integers when short integers will do. Don't use short
integers when bytes will do. This isn't terribly important where
individual variables are concerned, but pay careful attention to your
arrays.

Use globals

These days, programmers are taught to avoid globals for stylistic
reasons. However, they can actually be quite efficient, both in
terms of speed and size. There is a time and a place for everything.
If you are writing a program for a bank that 20 other programmers
are going to work on, you should avoid using globals. If you are
writing a game and you want to squeeze every drop of performance
out of it, use them liberally.

Use malloc() and free()

This is obvious. When an object, such as a bitmap or a sound effect,
is no longer in use, its space should be available for some other
object to use. A variation on this is to allocate all the free memory
at the beginning of your program, and control the use of it yourself.
Programmers often write functions called my_malloc() and my_free()
which simply keep track of pointers to this block of memory. However
you choose to manage memory, be careful about fragmenting it.

Reuseable arrays

Allocate an array of a few thousand bytes and use it over and over.
Temporary bitmaps, such as menu art, can go in there, along with
sound effects, masking maps, or whatever else comes and goes
frequently.

Write lots of small functions

Any time you do something more than twice, write another function
to handle it. The overhead of using many function calls can be
offset by the liberal use of global variables.

Don't use inline functions

Inline functions are the way to squeeze more speed out of a C++
program. In some cases, class functions default to inline. This
adds size to your program, because every occurrence of the function
call is expanded to the whole function at compile time. 

Use macros sparingly

Do this for the same reason you avoid using inline functions. The
macro substitution happens at compile time, adding size to your
executable program. Don't avoid macros altogether, though. They can
greatly simplify your code, and add a speed boost as well. Just
be careful how you use them.

Don't use unrolled loops

Unrolled loops are a speed optimization, at the expense of program
size. If size is a concern, roll them back up. A compromise solution
is to use partial unrolling. For example, you can execute a
1000-iteration loop 500 times but include the code twice within the
loop.

Don't use compiled bitmaps

Compiled bitmaps are all the rage these days. Bitmap data is turned
into assembly language instructions. They are blazingly fast, but
add massive size overhead to the size of your program.

Use itoa() instead of sprintf()

There are very few times when you want formatted text output in a
game. Usually the score will contain integers, and maybe some
inventory or map coordinates also involve numbers. The sprintf()
function does a nice job of converting numbers to text, but it
also does a lot more. The unneeded code adds to the size of your
program. If possible, avoid it and use itoa(). Similarly, use atoi()
instead of sscanf().

Don't use floating point

This is a universal truth in games, and programmers will go to great
lengths to avoid floating point math, including writing their own
"fixed point" functions, which are integer simmulations of real number
functions. In general, integer solutions can be found to most
game design problems, usually without resorting to fixed point.
For example, to turn clock ticks into seconds, you need to multiply
by 18.2. This can be accomplished by multiplying by 182 and dividing
by 10.

Rewrite the startup code

While I have never done this, some game programmers do it routinely.
The function c0.asm can be optimized by stripping out the code you
don't need.

Compress your executable

There is a freeware program called LZEXE which allows you to store
your executable in a compressed format which is decompressed at load
time. This saves disk space, but not RAM. The program will expand
to fill up the same amount of RAM it required before the compression.
There are also commercial programs that do the same job.

Use repetitive music and brief sound effects

Music and sound effects will eat up huge chunks of space. Plan these
carefully. Trim them down to the bare minimum, and store them in
RAM in reuseable arrays.

Turn little files into big files

Files are stored on disks in allocation units. Typically, DOS will
alocate a unit of two 512-byte sectors on a floppy disk, or four
sectors on a hard disk. A tiny file will fill the entire allocation
unit, even if it is only a few bytes long. The rest of the
allocation unit is simply unused space. On average, 512 bytes of space is
wasted for each file on a floppy disk. That means if you have 10
files on a floppy disk, you have approximately 5K of wasted space. So
if you can combine your bitmaps or sound effects into larger binary
files, you will achieve a significant savings in disk space.

Use reuseable artwork

If your title screen and your credit screen use the same artwork, but 
perhaps arranged differently, you can store it in a single file and
organize it at run time. If your title screen uses artwork from the
game, so much the better. Recycle those files as much as possible.

Strip the palette information out of PCX files

If you use the same color palette throughout the game, then you
don't need to store that information in a PCX file. The 256-color PCX
files have 768 bytes at the end which hold the palette information.
You can trim that off. If you have 10 PCX files all using the same
palette, this will save you more than 7K.

Use RLE bitmaps

Run Length Encoding (RLE) is a simple technique for compressing
bitmaps. It simply records the color, then the number of pixels of
that color, in a continuous pattern starting at one corner. Depending
on the artwork, this can save you considerable space both both in
terms of disk space and RAM. RLE's are also very efficient in terms
of speed. They can be displayed faster than an uncompressed bitmap,
but not as fast as a compiled bitmap.

Don't store text as graphics

If you have a page of text, for example opening credits or a storyline,
store it as character strings and display it using a bitmapped font.
Don't store it in a PCX file, which would be very wasteful.

Split your sprites

If you have two sprites that are almost identical, break them up.
For example, if you have a man with his hands up, and the same man
with his hands down, make the man one sprite, and the hands another
sprite. This will save quite a bit of sprite storage room, but
it causes a slight speed degradation (you turn one blit into two blits)
and it is also time-consuming and annoying to manipulate the artwork
that way. If you are at the end of the development cycle, and you
just need to squeeze a few more bytes out of your program, this is
a good place to do it.

As you can see, many of these tips are exactly the opposite of what
you would do if you were optimizing for speed. In actual practice,
game programmers tend to weigh their options and make decisions based
on both size and speed. These days, the equation is skewed heavily
toward speed, with size being a trivial factor in development
decisions. Still, it pays to know what you are trading away when you
make your tradeoff decisions. A good programmer will keep both size
and speed considerations in mind when designing and developing games.


