.op FASTER THAN BASIC =================== Ian Cull Bsc. 21/3/90. ---------------------- Part 4. Learning Machine Code by Example. ----------------------------------------- As has been mentioned in previous articles, the fastest programs are written by writing in machine code, or at least using an assembler (which makes the job a little easier). Unfortunately, it is not an easy task to teach yourself, and good teaching books are difficult to find. Hopefully, I can offer a solution. I have already looked at a Basic compiler, which takes a program written in (reasonably) easy to understand Basic, and converts it into machine code. A useful way to start learning machine code would be to examine the code produced by the compiler, compared with the original Basic. This is normally quite difficult, for two reasons. Firstly the code produced cannot be read without a disassembler, and secondly the code often uses many 'library' routines to do all but the simplest tasks, which makes the code difficult to follow. BASCOMP is a new Basic compiler, which can compile Basic programs into assembly text, instead of code. This means that it should be quite easy to understand the results, which should help in learning machine code programming. Here is an example Basic program :- 10 LET A=1: LET B=2 20 LET C=A+B 30 PRINT D The above rather simple program compiles, using BASCOMP, into the following text (shortened for clarity) :- JP X1 ;JumP past variables VA DEFS 2 ;Declare space for variables VB DEFS 2 VC DEFS 2 X1 CALL 3503;CLS ;CALL ROM to clear screen L10 LD HL,1 ;Get a value of 1 LD (VA),HL ;Store in variable A LD HL,2 ;Get a value of 2 LD (VB),HL ;Store in variable B L20 LD HL,(VA) ;Get what is in variable A PUSH HL ;Save it LD HL,(VB) ;Get what is in variable B POP DE ;Get variable A back ADD HL,DE ;Add the two together LD (VC),HL ;Store result in variable C L30 LD HL,(VC) ;Get what is in variable C CALL X10006;PRTINT ;CALL a routine to print it L0 RST 8 ;Stop with an 'OK' message DEFB 255 It should be quite clear that there is a definite relationship between the Basic program and the assembly text. Anyone with even a limited knowledge of the Z80 should be able to understand the above program, even if I had not added the comments. For those with no knowledge of the Z80, the assembly text may need additional explanation. The Z80 contains just a few 'registers' which can store numbers. These are called HL, DE and BC. If any further storage is needed, it must be declared within the program - the 'DEFS 2' instructions do this, giving us additional storage which has been called VA, VB and VC. The 'LD HL,number' instructions put the number into the HL register, ready for use. The 'LD (name),HL' instructions put whatever is in the HL register into an additional storage area (the brackets distinguish the storage from normal register names). The 'LD HL,(name)' instructions get back into the HL register whatever was last stored out. The 'PUSH HL' instruction puts whatever number is in the HL register onto the top of a pile of numbers. The 'POP DE' instruction gets the top number from the pile and puts it in the DE register. The 'ADD HL,DE' instruction (surprisingly) adds together the two numbers, putting the result into the HL register. The 'CALL somewhere' instruction makes the Z80 go off and do some other instructions, returning when the other instructions finish. The '3503' is a routine in the Basic ROM which clears the Spectrum screen. The 'X10006' is a routine supplied by BASCOMP which can display on the screen the number held in the HL register. Too Simple? ----------- You may be surprised at how simple the Z80 instructions are, simply putting numbers into various places and adding them together. These are the ONLY instructions that the Z80 knows! There is no multiply instruction, for instance. There isn't even a single instruction to print numbers or characters - many simple instructions are used to do such a seemingly simple task. However, machine code programs are very fast because each of the simple instructions that the Z80 knows can be carried out VERY quickly (in only a few millionths of a second!). The biggest problem with learning machine code is not learning a new language, but understanding how to break down what is to be done into instructions simple enough for the Z80 - this is what the compiler does. But is it any Faster? --------------------- The above example is very simple. Very little is done by the program - the slowest job is clearing the screen! Running the Basic program 100 times takes just 9 seconds. Running the machine code (after it has been assembled, of course) is quicker, taking 6.7 seconds. However, running a program which does nothing except clear the screen 100 times takes 6.7 seconds too! Therefore, what took 2.3 seconds in Basic now takes no time at all (or, at least, so little time as to be immeasurable). Bigger Basic programs, which do more work, show bigger increases. My two 'famous' test programs, PRIMES1 and PRIMES2, show increases of up to 100 times! 'BootStrapping'. ---------------- Here is an interesting fact. The BASCOMP compiler, which takes a Basic program and produces equivalent assembly text, is itself written in Basic! This is not as stupid as it might sound. BASCOMP is quite a simple Basic program really. It looks at the Basic program in memory and simply PRINTs equivalent assembly text, carrying out some simple translations if appropriate. The Basic 'GOTO 100' is PRINTed as 'JP L100'; a 'RETURN' is even more simply converted to 'RET' by BASCOMP! Therefore, if you know a little machine code, you can understand what each part of BASCOMP does. But the result is a complete Basic compiler. If you RUN BASCOMP so that it compiles itself, you end up with the assembly text which is a Basic Compiler program! A very difficult program to actually write in machine code, but quite easy to write in Basic. This process, where a program actually writes itself, is known as bootstrapping (for a strange historical reason which I will not discuss). Many computer languages use the same trick (FORTH compilers are written in FORTH, BCPL compilers in BCPL, etc). Learning more. -------------- The best way to proceed is to obtain a copy of BASCOMP and experiment with. BASCOMP is supplied by FORMAT, as a machine code program which will produce assembly source text, compatible with (almost) any storage system. All that is needed is a way of opening a stream to a disk file. Also supplied is BASASM, a single pass assembler which can convert the assembly source text into actual machine code, which can be run. Both BASCOMP & BASASM are written in Basic, and compiled using BASASM - the Basic versions of both are also supplied, which makes interesting study. I have tested BASCOMP successfully on microdrives, the Plus D system, a Spectrum Plus 3, and wafadrives. If demand is sufficient I will look further at BASCOMP & BASASM, explaining in detail how compilation & assembly is carried out, and how to improve things further (so that more Basic commands can be compiled, code produced will be quicker/smaller, etc). As a taster, I compiled & ran the PRIMES1 & PRIMES2 programs (printed in FORMAT Oct'89). Omitting the PRINT statements within the loop, I achieved times of 1.6seconds for PRIMES1 (20 times faster than 48K Basic), and 0.4seconds for PRIMES2 (100 times faster!).