Commodore Free Magazine, Issue 80 - Part 8
From
Stephen Walsh@39:901/280 to
All on Sat May 17 18:24:05 2014
ng sprite! Look!
Some enemies! Look! A scroller." Well, I had to test it myself at first,
but as soon as Pulse got sound I turned up the stereo to its max, and
everybody and their mothers squatted the joypad for at least an hour each.
I just took care that the collision detection was accurate or at least forgiving. It had to be a fair game. My flatmate scored 4271 points, by
the way.
- - - - - - - - - -
Q. Apart from Commodore Free have you had any other comments about the
game?
A. The folks at the VIC-20 Denial forum blew my hair back with their encouraging comments. Love you, too! They made me continue working on it
when it had no score counters or sound. I expected this to be another
piece of software of mine that'd get dumped into oblivion. But then I got cartloads of positive comments by very excited people and that was totally unexpected and scary. Pulse got a 100% rating on pouet.net. The Micro
Mart magazine, which is printed in Britain, told me that they'll publish a
news piece about it this week. I didn't read it yet but they already let
me know that they find the game most impressive. This thing went totally
out of control and beyond wildest dreams on special medication. This
possibly cannot happen again. That depression I mentioned is cured for
sure, though.
- - - - - - - - - -
Q. Imagine you could go back in time and were given the option to change
one part of the VIC. What would you change?
A. Full documentation and an assembler shipped with it instead of just a
BASIC handbook. It would still be a great educational toy for kids today
like that.
- - - - - - - - - -
Q. Do you have any question you would have liked to have been asked?
A. Not really. Thank you very much!
*************************************
OPTIMIZING CC65 CODE
What the Author Didn't Tell You
By Joseph Rose (a.k.a. Harry Potter)
*************************************
INTRO
Welcome to my cc65 C optimization documentation! Here, you will find some tricks and techniques to produce the best C code under cc65. Most of the techniques here will work for other 6502 targets, other C compilers, and
even other compilers and interpreters. I'm not yet an advanced programmer,
but I believe these optimizations to be useful. Some of these techniques
may be obvious to some, but you may still find something useful. This
document is organized into sections; each describes one technique. The individual techniques follow:
MIDDLEMAN
If your code needs to call the same function or group of functions, in
order and with some of the same parameters, you can use a "middleman,"
where the middleman will accept the call, and call the base function or functions for the callee and supply the constant parameters. This will
require some parameters to be passed only once in your code, making for
smaller code. An example follows:
Instead of:
extern int i[10];
int func2 (int ramcount,
enum machine m,
char* language)
{
...
}
void func (void) {
{
i[0]=func2 (64, machC64, "c");
i[1]=func2 (64, machC64, "BASIC");
i[2]=func2 (64, machC64, "assembler
");
i[3]=func2 (64, machC64, "FORTH");
i[4]=func2 (64, machC64, "Pascal");
}
Try:
extern int i[10];
int func2 (int ramcount,
enum machine m,
char* language)
{
...
}
int func2a (char* language)
{ return func2(64, machC64, language)
;}
void func (void)
{
i[0]=func2a ("c");
i[1]=func2a ("BASIC");
i[2]=func2a ("assembler");
i[3]=func2a ("FORTH");
i[4]=func2a ("Pascal");
}
CACHING VARIABLES
If you need a particular element in, for example, a multi-subscript array
of structs many times in your code, it is a good idea to read it once,
store the value in a local variable and access that instead. If you need
to access different members of the struct, assign the struct's address to a local pointer and use that to access the struct. This is made even better
if you use a zeropage variable.
MINIMIZE FUNCTION USAGE
Don't use functions you don't need. If you don't need the services of mprintf(), don't use it.
Examples follow:
My CBMSIMPIO library simplifies displaying text and numbers on the screen.
If you don't need the services of the standard screen output library and CBMSIMPIO can do the job, use CBMSIMPIO instead. This can save 2-3k in
your program.
If you need to copy memory from one location to another and the two never overlap, don't use memmove(). memmove() requires more overhead, and
memcpy() can do the job.
SYSTEM-SPECIFIC FUNCTIONS
If you're writing code for a specific target, use functions made for that target. This requires less overhead for conversion and otherwise makes for better code in general. An example is if you use file access with a CBM
model, using the CBM OS functions to access the OS directly.
CBM CONTROL CODES
The good thing about CBM screen output is that it can contain control codes
to perform functions such as change color or clear screen. If you need to,
for example, clear the screen before writing some text, including a clear screen code in the text can save from an explicit clrscr() call and shave 4 bytes from your code.
ASSEMBLER
Most programs can be created solely in C. However, some programs may
require at least some assembler. When deciding to use assembler in your
code and where, keep the following in mind:
* C is a medium-level and is good for calculations and program flow.
* Assembler is a low-level language and is good for data-crunching, hardware-manipulation and OS calls.
* If C can do the job immediately, you should use C.
* If C needs to do a work-around to do the job, you may want to use
assembler.
* If you want or need full control over hardware or the computer, you
should use assembler.
Don't be afraid to use assembler. It can be beneficial if used properly.
ASSUMING PARAMETERS
This is similar to the Middleman optimization. If a function only needs
one value for a particular parameter, remove the parameter and replace it
with the value. Then, remove the parameter in the declaration, definition,
and calls to the function.
TOKENIZING CALCULATIONS
If you need to use the same calculations over and over, store the calculation(s) in one function each and call the function(s) as needed.
OPTIMIZING LONGS
On an 8-bit computer, longs are very slow and require a lot of code.
Fortunately, using pointers to longs seems to produce tighter code. I
think this is because it allows your program to handle words, while the compiler provides the routines to handle the longs referenced. This,
however, should slow down your code even more.
CALCULATE ONCE
If you need the result of a particular calculation several times, perform
it once.
USE SWITCHES
Switches are good for many possibilities. Switches load the value once and perform several comparisons on it, saving from the extra loads necessary
with ifs. The exception to this rule is a true/false case which works
better on ifs.
INCREMENTAL SWITCH RETURNS
When you use a switch to return an incremental value where each condition returns one more (or less) than the previous, reorganize the code by
putting the highest (or lowest if less than the previous), using an increment/decrement instead and remove the breaks on all except the last if necessary. An example follows:
Instead of:
char c=0, d;
switch (c) {
case 1: d=1; break;
case 3: d=2; break;
case 2: d=3; break;
case 4: d=4; break;
}
Try:
char c=0, d=0;
switch (c) {
case 4: ++d;
case 2: ++d;
case 3: ++d;
case 1: ++d; break;
}
TOGGLING BOOLS
If you know that, for example, b is a bool and either 1 or 0, toggling b
using b^1 is shorter and probably also faster than !b.
IFS WITHOUT ELSES
If you have a series of ifs, all of which are mutually exclusive (i.e.
only one will work anyway), exclude the elses. In this way, you avoid the extra jump over the next elses.
ASSIGNING
Assigning a value when it's first used can save an explicit load. Ex:
Instead of:
--- MBSE BBS v1.0.01 (GNU/Linux-i386)
* Origin: Dragon's Lair ---:- bbs.vk3heg.net -:--- (39:901/280)