Mouse In A Hole, the platform game the course builds in Xtal BASIC 4.2, running on the Tatung Einstein: the mouse mid-jump between platforms, two cats and the cheese
← Back to Courses
module
31

Faster

Introduction

Play the game for a minute and it is plain that it is not quick. The cats amble, and the mouse moves in steps. This section finds out exactly how slow it is, where the time goes, and how to get some back - by measuring, not by guessing. And it finds the limit: the point past which BASIC cannot go.

Timing A Pass

Every time round the game loop is one pass. To time a pass, count them, and look at the clock.

BASIC keeps a clock in TI$, as six digits: hours, minutes, seconds. Setting it to "000000" starts it from nothing. Add to line 130, and add line 189, for a moment:

130 CALL &E000:K=PEEK(&E047) AND 96:J=PEEK(&E040) AND 64:NP=NP+1:IF NP=1 THEN TI$="000000"
189 IF NP=301 THEN PRINT @2,4;"T=";TI$:END

The clock is set on the first pass, so the long wait while the screen is drawn does not count, and the program stops after three hundred passes and shows the time. Run it and leave the keys alone:

300 passes in 38 seconds

000038 is 38 seconds. Three hundred passes in 38 seconds is 127 thousandths of a second each - 127 ms - or about eight passes a second.

Now run it again holding X all the way through. It takes 1 minute 4 seconds: 213 ms a pass, under five passes a second.

Read the clock carefully. 000104 is not 104 seconds; it is one minute and four seconds - 64.

Where The Time Goes

To find out what one part of the loop costs, take it out and time again. Putting REM in front of a line turns it into a remark, and it is easy to take away again. Changing line 177 to 177 REM takes the cats out; the difference in time is what they cost.

Done for each part in turn:

Taken outStanding stillIts cost a passHolding XIts cost a pass
nothing38 s-64 s-
the cats (177)24 s47 ms48 s53 ms
the test for home (185)34 s13 ms59 s17 ms
the test for being caught (178)37 s3 ms62 s7 ms
the cheese (390 made RETURN)38 s058 s20 ms

Three things stand out. The cats cost more than anything else. The test for home costs 13 ms a pass - every pass - for something that cannot possibly happen until the cheese is gone. And the cheese costs nothing at all while the mouse stands still, because S26 only looks for it after a move: that is what a cheap question first is worth.

The last surprise is the one you can do least about. Everything costs more while you hold a key.

What Things Cost

Timing single statements the same way - ten thousand of each - gives the prices, in thousandths of a second:

ms
A simple statement, such as A=A+1about 2
An IF whose test is falseabout 2
POINTabout 3
Moving a sprite with SPRITEabout 3.5

And some things that do or do not make a difference:

  • The order the variables were made in. BASIC finds a variable by looking through all the ones it has, starting with the first one made. A variable made after forty others takes far longer to find: with forty in front, A=B ran four times slower. The Einstein Compendium put it plainly in 1984: "The most commonly used variable should be assigned first."
  • Long names cost time. MOUSEX=MOUSEX+1 takes 3.5 ms where A=A+1 takes 2. The game's short names - MX, MY, K - were chosen for this.
  • % makes it slower, not faster. A%=A%+1 takes 2.6 ms.
  • Spaces cost nothing, and neither does splitting a line into several. Write what reads well.
  • A REM line that the program runs through costs about 1 ms each time.
  • A GOSUB has to find its line, and takes longer the more lines it passes on the way - about 6 ms longer for every hundred lines. Going forward it starts looking from where it is; going backward it starts from the very beginning of the program. So a subroutine used on every pass belongs close below the loop, and the loop belongs near the top. The game is already laid out that way: the parts used once, the shapes and the field, are at the end.
  • A held key slows the whole machine - by about two thirds, whatever the program is doing, and whether or not it reads the key. That is the Einstein's own handling of the keyboard, and nothing a program can switch off.

Four Savings

Make the busy variables first. The game's busiest variables - the keys, the mouse, the cats - are made late, after dozens of others that the shape subroutines made at the start: R, B, V, S$ and the rest. So give them values in line 15, straight after CLEAR, and make the cheese arrays there too, before anything else:

15 CLEAR &DFFF:K=0:J=0:G=0:VX=0:MX=0:MY=0:VY=0:FC=0:CT=0:CX1=0:D1=0:CY1=0
16 CX2=0:D2=0:CY2=0:I=0:C=0:GOT=0:LIVES=0:DIM EATEN(5),CHX(5),CHY(5):GOSUB 1710

The arrays are now made in line 16, so line 35 must go. Standing still: 38 seconds down to 31.

Skip what cannot apply. On the ground with no key down, the ground subroutine works out that VX is 0 and returns, having done nothing. Do not call it at all:

140 IF G THEN IF K OR J THEN GOSUB 300

OR works on the bits of the numbers, as AND does, so K OR J is non-zero - true - if either is. 28 seconds.

Ask the cheap question first. A false IF skips the rest of its line. So split the test for home into two IFs, the one that is nearly always false first:

185 IF GOT=5 THEN IF ABS(MX-167)<6 AND ABS(MY-138)<4 THEN 210

Until the cheese is gone, BASIC looks at GOT=5, finds it false, and moves on. 25 seconds.

Do half the work, twice as well. The cats cost the most. Move one cat each pass, taking turns, and move it twice as far:

250 CT=NOT CT:IF CT THEN 265
255 CX1=CX1+D1+D1:IF CX1<=20 OR CX1>=92 THEN D1=-D1:SPRITE 1,CX1,CY1,8,138-D1
260 SPRITE 1,CX1,CY1:RETURN
265 CX2=CX2+D2+D2:IF CX2<=150 OR CX2>=222 THEN D2=-D2:SPRITE 2,CX2,CY2,13,138-D2
270 SPRITE 2,CX2,CY2

CT flips between true and false every pass, so the red cat moves on one pass and the magenta cat on the next. Each moves four pixels instead of two, so they keep their speed. The limits change because the cats now land on every fourth pixel: counting in fours from where it starts, the red cat arrives at 92, never 94, and the magenta one at 222. 22 seconds.

Standing still, the game has gone from 127 ms a pass to 73 - more than fourteen passes a second instead of eight. Holding X, it has gone from 213 ms to 160.

The cats move the same distance each pass as before, and passes are quicker while the mouse stands still - so the cats walk faster while you wait.

As Fast As BASIC Goes

That is about as fast as this game will go in the BASIC you have been typing. There are smaller savings still to find, and none of them changes the picture: with a key held, the mouse moves six or so times a second, and every step it takes is visible.

It is not the game's fault, and it is not yours. BASIC reads your program as it runs it - finds each line, looks up each variable, works out each number - and all of that takes time, every pass. Held keys make it worse, and there is nothing a program can do about that.

There is one step left that keeps the game in BASIC: Xtal's own compiler, which does much of that reading once, before the game starts, instead of on every pass. S33 uses it on the finished game, and it roughly doubles the speed.

To make this game really smooth, though, it would be written entirely in assembly language - the Z80's own instructions, like the 28 numbers of the key reader, but the whole game. Many games of the time were written that way for exactly this reason. What BASIC gives you instead is a program you can read, change and try again in seconds, and that is what this course has been using it for. S35 shows you the first step towards the other kind.

The Code

Change lines 15 and 16, delete line 35, change lines 140 and 185, and replace the cats' patrol, lines 250 to 270. Put line 130 back as it was, and delete 189 by typing 189 and ENTER.

15 CLEAR &DFFF:K=0:J=0:G=0:VX=0:MX=0:MY=0:VY=0:FC=0:CT=0:CX1=0:D1=0:CY1=0
16 CX2=0:D2=0:CY2=0:I=0:C=0:GOT=0:LIVES=0:DIM EATEN(5),CHX(5),CHY(5):GOSUB 1710
130 CALL &E000:K=PEEK(&E047) AND 96:J=PEEK(&E040) AND 64
140 IF G THEN IF K OR J THEN GOSUB 300
185 IF GOT=5 THEN IF ABS(MX-167)<6 AND ABS(MY-138)<4 THEN 210
250 CT=NOT CT:IF CT THEN 265
255 CX1=CX1+D1+D1:IF CX1<=20 OR CX1>=92 THEN D1=-D1:SPRITE 1,CX1,CY1,8,138-D1
260 SPRITE 1,CX1,CY1:RETURN
265 CX2=CX2+D2+D2:IF CX2<=150 OR CX2>=222 THEN D2=-D2:SPRITE 2,CX2,CY2,13,138-D2
270 SPRITE 2,CX2,CY2

Then DEL 35,35.

Starting from

LOAD "MOUSE13" from S30.

What you should see

The same game, noticeably brisker - most of all while the mouse is still.

Save this. SAVE "MOUSE14".

Change One Thing

  • Put the timing lines back, and time the game with X held down all the way through. How does it compare with standing still?
  • Take IF K OR J THEN out of line 140, so the ground subroutine is called on every pass again, and time it standing still. What was that one test saving?
  • Put the cats back to moving together - S27's lines 250 to 280 - and time it. Which of the four savings was worth the most?

Exercises

31.1 Find out how much the new line 15 is worth on its own. Time the game three ways: with S30's lines 15 and 35 put back (and no line 16); with S31's line 15 but line 16 cut down to 16 DIM EATEN(5),CHX(5),CHY(5):GOSUB 1710; and as S31 has it. Which variables in line 15 earn their place?

31.2 Time FOR I=1 TO 1000:NEXT I on its own. How long does a pass of an empty loop take?

31.3 Find out whether GOTO behaves like GOSUB: time a GOTO that jumps forward over fifty REM lines against one that jumps over none.

Worked solutions are in Appendix II.

When It Goes Wrong

SymptomCause
The time is far bigger than expectedTI$ is hours, minutes, seconds. 000104 is 64 seconds.
The time includes the drawing of the screenThe clock is set before the loop. Set it on the first pass, as line 130 does.
The game stops after 300 passesLine 189 is still there.
Dimension Error in 35Line 35 is still there: the arrays are now made in line 16.
A cat hangs over the end of its platformIts limit is 94 or 224; with steps of four it must be 92 or 222.
The game is no fasterThe variables in line 15 must come before anything else - check that line 15 is the first line that runs after the REM.

Summary

Time a program with TI$, counting passes, and find what each part costs by taking it out. In this BASIC every statement costs about 2 ms and POINT and SPRITE a little more; long names and % cost extra and spaces cost nothing; a GOSUB pays for every line it passes, and every variable is found by searching the ones made before it. The biggest savings come from making the busy variables first and from not running lines at all: the cheap question first, and no work that cannot matter. Past that, the answers are the compiler, and then machine code.

Next

S32, Finishing Touches - a title screen, cats that start somewhere different each game, a timer, and the best time kept on the disc.

Get the Newsletter

New guides, disk images and community finds, roughly once a quarter. No spam, we promise, this isn't Tatung's marketing department.
Your subscription could not be saved. Please try again.
Your subscription has been successful.

Newsletter

Subscribe to our newsletter and stay updated.