Optimisation and Shells

by Ciaran McCreesh
Created: 6th December 1999
Last Modified: 15th December 1999

This page will cover some of the more common optimisation tricks that are used. These are mostly just short, one or two instruction routines. It will also explain shell titles, code size and t-states.

Why Optimise?

Optimisation is making programs run faster and/or use less memory. One common side effect of optimisation is loss of code legibility, so comment your programs, even if you won't be releasing the source! I say this now because as soon as you start serious optimising you'll be tearing your hair out trying to figure out exactly what your own code does.

There are a number of reasons to optimise code. Firstly there is the code size issue. Programs that take less memory leave room for more games, sorry, sensible work programs (smirk), on your calculator. Secondly there is the code speed issue. A few years ago everyone was using ASCR to draw sprites. About a year ago ZMR was released and the standard of games increased dramatically, just because the routine was twice as fast. Although you probably won't be writing routines that are that widely used, the standard of your programs will be much higher if you spend time optimising your code. Of course, the real issue is when people start reading your code (using an emulator if you don't release the source) and they notice cp 0 in your code they'll not take you seriously.

The two most common terms used in optimisation are bytes and t-states. Bytes are just how much space in memory an instruction takes. T-states are how long it takes the processor to run the given code (most people call these 'clock cycles', which, strictly speaking, aren't quite the same thing). As the CPU runs at approximately 6MHz (6000000Hz) it takes on average (number of t-states / 6000000) seconds to run a piece of code.

Basic Tricks

Optimisation can be a very time consuming process. You can sit for hours counting bytes and t-states of dozens of variations of the same piece of code (Assembly Studio contains a utility to do this for you) and end up with a millisecond faster routine that uses one byte less memory. Sometimes this is necessary. Mostly it is a waste of time. Some people take it to the other extreme. Anyone who's played Dying Eyes for the ti85 (can also be run on the ti86 if you know what you're doing) will know that it takes up most of your memory (~20Kb) and is very slow (it's still a good game though).

There are some tricks you can use to produce faster code with very little effort. The most common of these are listed below.

Comparing to Zero

The chances are, if you've wanted to decrement a register and jump somewhere if it is zero (or not zero) you'll do this:

  dec e
  ld a,e
  cp 0
  jp z,SomeWhere

However, you only need to do this:

  dec e
  jp z,SomeWhere

People often forget (or don't know) that almost every instruction sets the flags (the most common ones that don't are the push and pop instructions) depending on the result. You rarely have to include the cp instruction at all.

If you want to compare the accumulator (the a register) to zero and can't use the above technique you can still probably avoid using cp 0 - a much faster alternative that is also a byte smaller is or a. This works because (a or a) always equals a, and this instruction also sets the flags.

Finally, djnz is not restricted to loops.

Jumps and Calls

Always remember the two types of jump - relative (jr) and absolute (jp). Absolute jumps take a byte more, but are slightly faster. It's up to you which you use, but I'd stick with relative jumps where possible as the few t-states difference mean little.

If you're going to jump to the same memory location from lots of different bits of code there's a jp hl (Assembly Studio, TASM and certain versions of the z80 technical data use jp (hl), but it does the same thing) instruction which takes up less memory and is faster. If you need hl you could use jp ix (or jp (ix)) or jp iy (or jp (iy)) instead.

If you're going to call something and then ret immediately afterwards you might as well just jp to whatever you want to call.

Setting a to Zero

Don't use ld a,0. Use xor a.

Clearing the Screen

Use call _clrScrn at the end of your program as it does more than clear the screen - it also clears the text shadow (an area of memory that contains whatever's on the home screen). If you want to clear the screen frequently during the middle of your program you could call the following instead:

ClearScreenFast:
  ld hl,$fc00
  ld (hl),l
  ld de,$fc01
  ld bc,1023
  ldir
  ret
Multiplying or Dividing by Two

Consider the shift and rotate instructions (see the instruction reference) for multiplying and dividing by two. Also, to divide a by 16, for example, you could sla four times or you could rlca four times and then and %00001111.

Loading The Same Number

It is faster and takes one byte less memory to load a register with the value of another register than to load a register with a number.

Shell Headers

If you've used a shell you've probably seen that some programs have 'titles'. If not, look at the screenshots below (they're in blue rather than green because I've got my emulator set that way - I can see greyscale better like that).

Screenshot showing ASE titles Screenshot showing Rascal titles
ASE titles Rascal titles
Screenshot showing YAS titles Screenshot showing Tinux titles
YAS titles Tinux titles

These shells share a common method for finding a program's title. They all compare the first few bytes of the program file for a certain pattern. If this pattern exists they carry on reading to get the program's title. A very simple program with a shell title is given below. Note that 179 is the ti86 character code for lowercase c cedilla (ç).

#include "ti86asm.inc"
.org _asm_exec_ram

;Shell Title
  nop
  jp ProgStart

.dw 0, ShellTitle

ProgStart:
  call _clrScrn           ;clear screen
  call _homeup            ;cursor to top left
  
  ld hl,ShellTitle        ;display program name
  call _puts
  call _newline
  
  ld hl,MoreText          ;display more stuff
  call _puts
  call _newline
  
  call _pause             ;wait for [ENTER]
  
  call _clrScrn           ;clear screen and exit
  jp _homeup

ShellTitle:
.db "Tux Le Pinguoin!",0

MoreText:
.db "C'est beau, ", 179, "a!",0

.end
Screenshot showing program in Tinux Screenshot showing program being run
Shell Title in Tinux Program Being Run