.........1.........2.........3.........4.........5.........6.........7.........8 Mastering Machine Code on Your Spectrum part 5 of 8 - from ZX Computing Jun/Jul'83 Machine code master, Toni Baker, shows you how to incorporate machine code within your BASIC programs with an incredible program to add visual accompaniment to your stereo. I'd like to cheat a bit now, if I may, by giving you a BASIC program. (Shrieks of horror!) Well, it has got some machine code in it, but nothing new. This is a lesson in how to incorporate machine code into BASIC, for finding a use for all these weird and wonderful routines that keep cropping up. Almost the hardest task you have in programming is the hanging around at bus stops and tedious dinner parties waiting for that most illusive of qualities - inspiration. The machine code routine in question was featured in part three of this series of articles - it's a routine to change the colours of PAPER and INK throughout the whole screen faster than you can say 'The sixth sick sheik's sixth sheep's sick' without falling over. The BASIC that surrounds it is new, however. This is a program to impress your next door neighbours and fanatics of laser shows, or a new way of running a disco. This is visual accompaniment to your stereo! Sound's great! It's not technically a sound- to- light unit, for the program has no way of knowing whether or not your stereo is even switched on, let alone what's playing on it. What it is, however, is a very good optical illusion of sound and vision being synchronised. What you do is RUN the program and input answers to the various questions asked, put your favourite record on, switch the lights off and close the curtains, then just sit back while your brain dances round in circles. The program is featured in Figs. 1 and 2. You should enter the machine code first (using a BASIC program to do so) and then delete this BASIC program to input the one given. Take a look at this now. I'll now give you some examples on how to RUN the program. Type RUN. (Easy so far, isn't it?) For the question 'NUMBER OF LINES', you should input (say) 5. For 'INK', input 'INT (4*RND)+4', and for 'PAPER' input '0'. Finally, for 'STARS?' you should input 'Y'. This is just an example - try it out for yourself. What's the plot? The program has a couple of extra features which you ought to know about. RUN 200 enables you to define the initial INK and the initial PAPER colours. For instance, RUN 200 and then input 0 / 7 / 4 / '7-X' / '7-Y' / 'Y' / ('/' counts as ENTER). RUN 400 will SAVE and VERIFY the program and the machine code. I won't turn the above program into machine code just yet. Before I do, I'll give you some information on PLOT and DRAW. CALL PLOT_BC (Hex CD E522) requires B to hold the Y co-ordinate, and C to hold the X co-ordinate. This will PLOT the required point. Bit zero of P_FLAG (5C91) must be zero for OVER 0 or one for OVER 1. CALL DRAW_3 (Hex CDBA24) requires B to hold the absolute value of the Y parameter, and C the absolute value of the X parameter. If Y is greater than or equal to zero then D should hold 01 Hex, otherwise D should hold FF Hex. If X is greater than or equal to zero then E should hold 01 Hex, otherwise E should hold FF Hex. The sequence of instructions LD HL,2758 / EXX must be carried out between CALL DRAW_3 and RET (to BASIC) otherwise the Spectrum will crash. Transformation time We still can't rewrite the program into machine code yet, since we don't have a routine for INT (X*RND). It is possible to write a simple random number subroutine which creates random numbers between 0000 and FFFF Hex in the HL register pair, so take a look at Fig.3 which illustrates such a subroutine. It works using the random number seed already used by the ROM, but does not actually call the ROM's RND routine (since this is highly impractical). The subroutine does, however, work in more or less the same way as RND - it takes the value of the system variable SEED, and multiplies it by some constant. Only the remainder modulo 65536d is taken as the new result. This is not quite RND, but it does give fairly random results. Figure Four, on the other hand, is another kettle of fish altogether. This is a subroutine I've called RAND_A in order to distinguish it from the subroutine in Fig.3. It requires that A contains a number between 00 and A-1. Of particular note is the subroutine MULT which is called from within RAND__A; this is a subroutine which will multiply the number held in the A register by the number held in the DE register pair. The result will be formed in the AHL register triplet (the result will always fit in three bytes). RAND_A works just like the ROM's RND routine - it takes a value, SEED, which is multiplied by 4B, and then the remainder is found from a division by Hex 10001, less one. Can you see how the program calculates this remainder? To find the random number required, this new seed is multiplied by A and the high part only becomes the random number. Now we are almost ready to turn the program Patterns entirely into machine code. Well - not quite entirely as we still can't do VAL. Let's ignore that part for the time being and leave VAL in BASIC. I'd like you now to study Figs. 5 and 6, which list the revised program, now called Patterns 2. If you now compare it with Figs. 1 and 2, see if you can work out how the translation is achieved. Note that the instruction CALL RAND_A is used - this is a reference to the subroutine in Fig. 4. You can use any addresses you like for this program. I chose to use the following: Label Hex Dec RAND_A 7000 28672 MULT 7021 28705 FIND_A 7030 28720 PL_DR 703B 28731 INIT 7078 28792 MAIN 7098 28824 P_VAL 7105 28933 P_OVER 7106 28934 P_DRAW 7107 28935 LINES 7108 28936 INK 7109 28937 PAPER 710A 28938 STARS 710B 28939 ARRAY 710C 28940 It is essential, however, that P_OVER and P_DRAW be adjacent, and also that ARRAY points to the start of a segment of spare RAM. - Fig.1 The program, Patterns: part one - the BASIC - - - - - - - - - - - - 10 INPUT "NUMBER OF LINES";n 20 DIM x(n+1): DIM y(n+1) 30 INPUT "INK";x$ (use the keyword INK here) 40 INPUT "PAPER";y$ (use the keyword PAPER here) 50 INPUT "STARS?";a$: LET s=a$="N" OR a$="n" 60 CLS: LET p=1 70 LET p1=p+1: IF p1>n+1 THEN LET p1=1 80 LET p2=p1+1: IF p2>n+1 THEN LET p2=1 90 PLOT x(p1),y(p1) 95 DRAW x(p2)-x(p1),y(p2)-y(p1) 100 PLOT OVER s;x(p1),y(p1) 105 DRAW OVER 1;x(p2)-x(p1),y(p2)-y(p1) 110 LET x(p1)=INT (256*RND) 120 LET y(p1)=INT (176*RND) 130 PLOT x(p),y(p) 140 DRAW x(p1)-x(p),y(p1)-y(p) 150 LET x=VAL x$: LET y=VAL y$ 160 POKE 32769,x: POKE 32770,y: LET p=p1+USR 32768 170 GO TO 70 200 INPUT "INITIAL INK";x 210 INPUT "INITIAL PAPER";y 220 GO TO 10 400 SAVE "PATTERNS" LINE 430 410 SAVE "PATTERNS" CODE 32768,34 420 VERIFY "": VERIFY "" CODE: STOP 430 CLEAR 32767: LOAD "" CODE: STOP [I added the CLEAR. JG.] - Fig.2 The program, Patterns: part two - the machine code - - - - - - - - 01???? PATTERNS LD BC,???? To be POKEd by BASIC 78 LD A,B 76 HALT D3FE OUT (FE),A 78 LD A,B 87 ADD A,A 87 ADD A,A 87 ADD A,A 81 ADD A,C 218D5C LD HL,ATTR_P 77 LD (HL),A 210058 LD HL,ATTRS 110158 LD DE,ATTRS+1 01FF02 LD BC,02FF 77 LD (HL),A EDB0 LDIR C9 RET - Fig.3 The machine code random number subroutine - - - - - - - - - - - - - D5 RAND PUSH DE 2A765C LD HL,(SEED) 54 LD D,H 5D LD E,L 29 ADD HL,HL 29 ADD HL,HL 19 ADD HL,DE 29 ADD HL,HL 29 ADD HL,HL 29 ADD HL,HL 19 ADD HL,DE 22765C LD (SEED),HL D1 POP DE C9 RET - Fig.4 The INT(A*RND) routine - - - - - - - - - - - - - - - - - - - - - - C5 RAND_A PUSH BC D5 PUSH DE E5 PUSH HL F5 PUSH AF 3E4B LD A,4B ED5B765C LD DE,(SEED) CD???? CALL MULT A7 AND A 4F LD C,A ED42 SBC HL,BC 3801 JR C,AA 2B DEC HL 22765C AA LD (SEED),HL 54 LD D,H 5D LD E,L F1 POP AF CD???? CALL MULT E1 POP HL D1 POP DE C1 POP BC C9 RET 0608 MULT LD B,08 210000 LD HL,0000 29 LOOP ADD HL,HL 17 RLA 3003 JR NC,BB 19 ADD HL,DE CE00 ADC A,00 10F7 BB DJNZ LOOP C9 RET - Fig.5 The program, Patterns2; the BASIC - - - - - - - - - - - - - - - - - 10 INPUT "NUMBER OF LINES";n 20 POKE lines,n: LET n=USR init 30 INPUT "INK";x$ 40 INPUT "PAPER";y$ 50 INPUT "STARS?";a$: POKE stars,a$="N" OR a$="n" 60 POKE ink,VAL x$: POKE paper,VAL y$ 70 LET n=USR main 80 GO TO 60 200 INPUT "INITIAL INK";x 210 INPUT "INITIAL PAPER";y 220 GO TO 10 400 SAVE "P2" LINE 450 410 SAVE "P2" CODE rand_a,263 420 VERIFY "" 430 VERIFY "" CODE 440 STOP 450 CLEAR rand_a-1: LOAD "" CODE [I added the CLEAR. JG.] - Fig.6 The program, Patterns2; the machine code - - - - - - - - - - - - - D5 FIND_A PUSH DE 11???? LD DE,ARRAY 6F LD L,A 2600 LD H,#00 29 ADD HL,HL 19 ADD HL,DE D1 POP DE C9 RET C5 PL_DR PUSH BC D5 PUSH DE 7A LD A,D D5 PUSH DE CD???? CALL FIND_A 4E LD C,(HL) 23 INC HL 46 LD B,(HL) C5 PUSH BC 3A???? LD A,(P_OVER) 32915C LD (P_FLAG),A CDE522 CALL PLOT_BC C1 POP BC D1 POP DE 7B LD A,E CD???? CALL FIND_A 7E LD A,(HL) 23 INC HL 1E01 LD E,#01 91 SUB C 3004 JR NC,PD2 ED44 NEG 1EFF LD E,#FF 4F PD2 LD C,A 7E LD A,(HL) 1601 LD D,#01 90 SUB B 3004 JR NC,PD3 ED44 NEG 16FF LD D,#FF 47 PD3 LD B,A 3A???? LD A,(P_DRAW) 32915C LD (P_FLAG),A CDBA24 CALL DRAW_3 D1 POP DE C1 POP BC C9 RET 210040 INIT LD HL,D_FILE 110140 LD DE,D_FILE+1 01FF17 LD BC,#17FF 3600 LD (HL),#00 EDB0 LDIR ED4B???? LD BC,(LINES-1) 04 INC B 21???? LD HL,ARRAY AF XOR A 77 IN2 LD (HL),A 23 INC HL 77 LD (HL),A 23 INC HL 10FA DJNZ IN2 32???? LD (P_VAL),A C9 RET ED4B???? MAIN LD BC,(P_VAL) 3A???? LD A,(LINES) 51 LD D,C 14 INC D BA CP D 3002 JR NC,M2 1600 LD D,#00 5A M2 LD E,D 1C INC E BB CP E 3002 JR NC,M3 1E00 LD E,#00 210000 M3 LD HL,#0000 22???? LD (P_OVER),HL CD???? CALL PL_DR 3A???? LD A,(STARS) 32???? LD (P_OVER),A 3E01 LD A,#01 32???? LD (P_DRAW),A CD???? CALL PL_DR 7A LD A,D CD???? CALL FIND_A 3EFF LD A,#FF CD???? CALL RAND_A 77 LD (HL),A 23 INC HL 3EB0 LD A,#B0 CD???? CALL RAND_A 77 LD (HL),A 5A LD E,D 51 LD D,C 210000 LD HL,#0000 22???? LD (P_OVER),HL CD???? CALL PL_DR 7B LD A,E 32???? LD (P_VAL),A 76 HALT 3A???? LD A,(PAPER) D3FE OUT (#FE),A 87 ADD A,A 87 ADD A,A 87 ADD A,A 2A???? LD HL,(INK) B5 OR L 328F5C LD (ATTR_T),A 210058 LD HL,ATTRS 110158 LD DE,ATTRS+1 01FF02 LD BC,#02FF 77 LD (HL),A EDB0 LDIR 215827 LD HL,#2758 D9 EXX C9 RET -- Another Fine Product transcribed by: Jim Grimwood (jimg@globalnet.co.uk), Weardale, England --