Wednesday 9 January 2013

Getting Speed Out of MC-10 Microsoft Basic

Over the past few years I've slowly learned about, or stumbled my way to, a bunch of techniques for getting a lot of speed out of the Microsoft basic that comes with the MC-10.

The main GOSUB routines are usually grouped in the 1-19 line range.  I can't remember who originally taught me to do this or whether I figured it out on my own somehow, but it was Neil Morrison on the MC-10 yahoo forum who drove the point home when he commented:

> One of the most important things is to put all of the GOSUB and GOTO destinations in the first lines of the > program. You can actually put a jump to the end of the program, DIM variables there,
> and then jump to where you start.
>
> When you use a GOSUB or GOTO the program will begin at the first line and check every line of the
> program until it hits the line number used. You want to make that search time as short as you can.
>
> So IIRC, your program reads like
>
> 0 GOTO 2000
> 10 RETURN
> 20 RETURN
> 30 RETURN
> ...
> 100 ' Program start
> ...
> 2000 A=1:B=3:C=7.5 .....
> ...
> 2100 GOTO 100
>
> All tricks we used at the time.
>
> Neil

I also started to use a main FOR/NEXT loop usually occuring around line 20 instead of GOTOs.  And when I use FOR/NEXTs, I leave off the variable designation for the NEXT command (i.e. NEXT A), since this is unnecessary and simply slows down the interpreter.  By structing my programs using FOR/NEXT loops instead of GOTOs, and using ON/GOSUB/GOTO statements instead IF/THENs, and by packing lines as tightly as possible (which can be done completely on the MC-10 up to 128 characters length and almost completely in Coco Basic up to 256 characters in length), I have managed to get quite a turn of speed out of old un-compiled Basic.

I also, generally use memory heavily by doing as many of the repetitive calculations at initialization as possible and then store the results in arrays. This often means a long start-up delay for a program, but much faster game play when things finally get going.  In attempting to create a fast "side scroller" game like my RAIDER this was critical.  It calculates all the ups and downs for the random cavern generation before each round and then saves these in an array, so that when the screen is printing the scrolling cavern all it has to do is use MID to print the current 32 character long strings with one character less, and then plot the new part of the cavern on the left side of the screen.
If I'm manipulating strings, I would always try to CLEAR as much memory as possible to cut down on the garbage collection delays. The MC-10 boots allocating only  about 100 bytes of string space, but using the CLEAR command you can designate more. An individual string variable can be assigned a string up to 255 characters in length, regardless of what the manual says. However, since you can only input program lines that are 127 lines long you can't directly assign strings greater than 127 (or even less, since you must also include a line number and the assignment A$="). It can be larger if you generate a string some other way, say by constructing it from data read from data statements. However, if you are concatenating a lot of strings (A$=A$+B$) you must clear enough extra memory to allow the MC-10 to do all the concatenations. That is A$=A$+B$ requires that you have extra space allocated for A$ so that the MC-10 can store it and then store it again but with B$ added. The MC-10 will then "garbage collect" the space of the original version of A$ and clean up the string space behind the scene to allow that space to be used again by other string variables. I guess you could say you need to clear enough space for all string "handling" rather than just all the strings you intend to use. However, as Mechacoco (Darren) from the Yahoo group also notes:
Strings which are only assigned simple literal values within a program line do not require any string space. Neither do variables which are assigned as an exact copy of another. For example, the following will work:
10 CLEAR 0
20 A$ = "HELLO WORLD"
30 B$ = A$
40 PRINT B$

...but this will produce an ?OS ERROR in line 40:
10 CLEAR 0
20 A$ ="HELLO"
30 B$ = " WORLD"
40 C$ = A$+B$
For this to work, you would need to CLEAR at least 11 bytes for string space.
- Darren
Managing string space so that the MC-10 had the maximum space for doing its garbage collection was critical for a program like my attempt at a 3D first person tank shooter game like KURSK, which is always cobbling together your current view (from string arrays) in any of the four possible directions you can turn your turret:
It's been an intriguing challenge refining these techniques, which I have mostly gleaned from the comments of helpful folks on Yahoo, or from around the Net over the years.  One technique that has been especially helpful was described very well by J Diffendaffer on the Yahoo forum
Yeah, the variables go into the list in the order you create them and placing the most used ones first means they get found faster.  I believe this was covered in the book 'BASIC Faster and Better'. There is a PDF version online somewhere.
All my programs now use DIM to not only declare arrays, but all variables that will be used in the program.  What's most important is to put the most used variables first, since the variable names have to be searched through in a list ordered in terms of either when they are first declared in an initial DIM statement or first used by in the program.  So variables used intensely in the main loop (usually lines 20-30) should come first in the DIM statement.

I have almost completely stopped using IF statements except for non-critical mundane tasks like opening screens and menus.  Instead of IF, I use an ON/GOTO/GOSUB structures like this:

10 ON1-(A>B)GOTO20:REM ** PATH FOLLOWED IF TRUE **
20  ** THE PATH FOLLOWED IF FALSE **

A structure like this allows for much tighter packing of lines and just seems to work a lot faster than IF.  Since every space and new line of program must also be dealt with by the interpreter, getting rid of them is very helpful for increasing speed, and since the MC-10 doesn't have an ELSE statement, every IF statement effectively must hog its own line, so the above technique is a very useful one.  There are other ways of also using the pre-calculated arrays mentioned about more easily in such ON/GOTO and ON/GOSUB statements.  When I use an ON/GOSUB statement I also generally use multiple RETURNs to leap back directly into the main FOR/NEXT loop after that statement, rather than GOTOing to a single RETURN. In other words any RETURN is the same as any other, so I also generally have a line like 1 RETURN at the top of the program for use as a "null" option for any of my ON/GOSUB statements and their lists of subroutines.

Screenshots of most of the games I have created using these techniques can be found at:
https://github.com/jggames

No comments:

Post a Comment