.........1.........2.........3.........4.........5.........6.........7.........8 Mastering Machine Code on Your Spectrum part 1 of 8 - from ZX Computing Oct/Nov'82 Toni Baker, author of "Mastering Machine Code on Your ZX81" turns her attention to the Spectrum with this article, the first in a series designed to take you through machine code from its very beginnings to its ultimate conclusions. Inside the Spectrum is a tiny little black box mystically referred to as a "z80A". In fact the Z80A is the only part of the whole computer that actually does any thinking. To put it another way, the Z80A is the computer. The ROM is not a computer - the ROM just contains a computer program. The Z80A speaks a language we call MACHINE CODE. It does not speak BASIC. When you RUN a BASIC program, what's really happening is that the Z80A is running a program in the ROM which tells it to look at what's written down in the RAM and then take appropriate action. Machine code has variables just like in BASIC, but they're not quite as flexible. The registers are called A, B, C, D, E, H, and L, and they can only store integers from 0 to 255. It's easier to work in hexadecimal so I'll do that from the start - 00 means 0 and FF means 255. In general two symbols (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E or F) written next to each other means sixteen times the first digit, plus the second digit - leading zeroes are therefore optional - however DON'T waste your time converting things back to decimal all the time - you don't need to. 5A is obviously bigger than 3E because 5 is bigger than 3. In the same way D7 is a bigger number than AA. It is not necessary to change the numbers into decimal first - it is better to get a kind of "feel" for the size of a number in hex without actually knowing what it is. After all, that's all we do in decimal isn't it? I bet you can't imagine a pile of (exactly) seventy three pennies. A variable in machine code can therefore hold any number between 00 and FF. A machine code variable is called a REGISTER. There are no error traps in machine code, and so if you try to add up two numbers whose sum is more than FF you will get the wrong answer (in actual fact it will be 100 (hex) less than the real answer) - the last two digits will be the only ones that count. Take a look at this little segment of machine code: LD A,9A - This is like a LET statement. Register A now holds the number 9A. ADD A,88 - In machine code you can only do one thing at a time - you cannot, eg. say LD A,9A + 88 as you could in BASIC. What value does the A register now contain? The answer is 22. Try to do the adding up in hex: A plus 8 equals 2 carry 1; 9 pIus 8 plus the carry is also 2 carry 1; this carry is "lost". Registers can also be used in pairs. The only combinations allowed are BC, DE, and HL. If B contains 61 and C contains A7 then we say that BC contains 61A7. This is a four digit hexadecimal number. Its size is intuitively just a bit bigger than 6000, and a lot less than 7000. Similarly, if HL contains 1234 we say that H contains 12 and L contains 34. HOW do we actually USE machine code? When the ZX83 comes out, hopefully there will be a few buttons marked with machine code instructions. Until that happens we unfortunately have to do some translating. Each machine code instruction has a number - a sort of index. Instruction number one is LD BC, - something like a LET statement in BASIC. All the Z80A needs is a list of numbers. Whenever it comes across the number 01 it knows it has to carry out the operation LD BC; it also of course expects a four digit number next so that it knows what to load BC with. This index number is called a HEX CODE. The words we use for the instructions are sometimes called OP CODES (Operation Codes). For every OPcode there is a HEXcode, and for every HEXcode there is an Opcode. The computer needs the HEXcodes in its programs. Humans on the other hand find it easier to use the Opcodes. When writing down a machine code program on a piece of paper we usually then write BOTH versions next to each other - like this: C9 RET Here C9 is HEXcode which the computer will understand. RET is our way of writing it. RET means RETURN; either "Return to BASIC" as we shall use very shortly, or "Return from a subroutine" which I shall cover in a later article. Every machine code program you write, must end with a RET instruction. The meaning of USR USR is a function in BASIC - it's very much like a cross between a GOSUB statement and user defined (numerical) function. It looks very much like a functIon in appearance: USR X has the same "shape" as SIN X or INT X, and can be used in exactly the same circumstances. But if SIN X equals the height of a sine wave at position X, and if INT X equals X with all of its decimals banished, what number does USR X give us? ANSWER: USR X gives us the value of the BC register. A machine code program is run every time USR is used, and the number or variable or whatever after the word USR must be the address of the start of a program written in machine code. For example, consider this machine code program: 010000 LD BC,0000 C9 RET If X was the address of the "01" in the above, then what number would USR X give us? RET, remember, means return to BASIC, and so BC ends up as zero. In this case USR X would give us a value of zero, so PRINT USR X would print 0, and LET Y = USR X would assign Y with zero, and so on. Our next problem is how do we get the machine code into the computer in the first place? The only way to do it is with a BASIC program. Take a look at the program in Fig.1. It's called HEXLD, and I shall explain what it does and how it works. - Fig.1 HEXLD - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 10 DEF FN k(x)=CODE "01234567890000000:;<=>?00000000000000000000000000:;<=>?"(CODE a$(x)-47)-48 20 LET a$="" 30 INPUT x 40 IF a$="" THEN INPUT a$ 50 POKE x,16*FN k(1)+FN k(2) 55 PRINT a$(TO 2);" "; 60 LET a$=a$(3 TO) 70 LET x=x+1 80 GO TO 40 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - The first line is a user-defined function which turns a string character into a number. Its effect is to turn "0" into 0, "1" into 1, and so on until "9" which becomes 9. In addition "A" becomes ten, "B" becomes eleven, and so on up to "F" which becomes fifteen. Small letters are also taken into account and so "a" also becomes ten, and so on up to "f" which gives fifteen just as if it were a capital. The rest of the program is your HEX LOADER. To use the program type RUN, then input a (decimal) address. Input 24576 here (for no other reason than the fact that in hex 24576 is written as 6000). Now all you need to type in is your machine code. Type in "010000" and then "C9". To stop the program type in just a newline - this will cause error code 3. You now have a machine code program. Type PRINT USR 24576 to see if it gives zero as it should. If you want to see what you're doing change line 40 to read IF a$ "" THEN INPUT a$: PRINT a$ - Fig.2 The listing of HEXLD3 - - - - - - - - - - - - - - - - - - - - - - - 10 PRINT " LIST "; 20 GO SUB 8000 30 RANDOMIZE USR 65055 40 STOP 100 PRINT "WRITE "; 110 GO SUB 8000 120 INPUT a$: PRINT ; 130 RANDOMIZE USR 65152 140 PRINT a$ 150 GO TO 120 200 PRINT "INSERT "; 210 GO SUB 8000 220 INPUT a$: PRINT ; 230 RANDOMIZE USR 65109 240 PRINT a$ 250 GO TO 220 300 GO SUB 320 310 STOP 320 PRINT " DELETE "; 330 GO SUB 8000 340 PRINT " TO "; 350 GO SUB 9000 360 RANDOMIZE USR 65235 370 RETURN 400 SAVE "HEXLD3" LINE 460 410 SAVE "HEXLD3 MC" CODE 65016,353 420 SAVE " " CODE FN p(65016),FN p(65024)-FN p(65016)+1 430 VERIFY "" 440 VERIFY "" CODE 450 VERIFY "" CODE: STOP 460 BORDER 0: INK 7: PAPER 0: FLASH 0: BRIGHT 0 470 CLEAR 65015 480 LOAD "" CODE: LOAD "" CODE: STOP 500 PRINT "BEGIN "; 510 GO SUB 8000 520 RANDOMIZE USR 65264 530 STOP 600 PRINT "REPLACE "; 610 GO SUB 330 620 GO TO 220 700 PRINT " RUN "; 710 GO SUB 8000 720 RANDOMIZE USR FN p(65018) 730 STOP 800 PRINT " COPY ";: GO SUB 8000 810 PRINT " ";: GO SUB 9000 820 PRINT " TO "; 830 INPUT "ADDRESS ";a$ 840 PRINT "ADDRESS ";a$ 850 POKE 65022,16*FN k(a$,3)+FN k(a$,4) 860 POKE 65023,16*FN k(a$,1)+FN k(a$,2) 870 RANDOMIZE USR 65275 8000 INPUT "ADDRESS ";a$ 8010 PRINT "ADDRESS ";a$ 8020 POKE 65018,16*FN k(a$,3)+FN k(a$,4) 8030 POKE 65019,16*FN k(a$,1)+FN k(a$,2) 8040 RETURN 9000 INPUT "ADDRESS ";a$ 9010 PRINT "ADDRESS ";a$ 9020 POKE 65020,16*FN k(a$,3)+FN k(a$,4) 9030 POKE 65021,16*FN k(a$,1)+FN k(a$,2) 9040 RETURN 9050 DEF FN k(x$,y)=CODE "01234567890000000:;<=>?00000000000000000000000000:;<=>?"(CODE x$(y)-47)-48 9060 DEF FN h(x$)=4096*FN k(x$,1)+256*FN k(x$,2)+16*FN k(x$,3)+FN k(x$,4) 9070 DEF FN k$(x,y)="0123456789ABCDEF"(INT (x/16^y)-16*INT (x/16^(y+1))+1) 9080 DEF FN h$(x)=FN k$(x,3)+FN k$(x,2)+FN k$(x,1)+FN k$(x,0) 9090 DEF FN p(x)=PEEK x+256*PEEK (x+1) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Fig.3 The code for HEXLD3 - - - - - - - - - - - - - - - - - - - - - - - - FE02: F5 << [This line was missed out in the magazine. JG.] FE03: E6 FE59: 4E FEAF: 40 FF05: 44 FE04: F0 FE5A: 23 FEB0: 38 FF06: 4D FE05: 1F FE5B: 46 FEB1: 04 FF07: 03 FE06: 1F FE5C: C8 FEB2: E6 FF08: 2A FE07: 1F FE5D: 28 FEB3: DF FF09: FE FE08: 1F FE5E: CB FEB4: D6 FF0A: FD FE09: C6 FE5F: 19 FEB5: 07 FF0B: EB FE0A: 30 FE60: 20 FEB6: D6 FF0C: A7 FE0B: FE FE61: 02 FEB7: 30 FF0D: ED FE0C: 3A FE62: CF FEB8: 82 FF0E: 52 FE0D: 38 FE63: 14 FEB9: D1 FF0F: 19 FE0E: 02 FE64: C5 FEBA: 12 FF10: 38 FE0F: C6 FE65: 2A FEBB: 13 FF11: 04 FE10: 07 FE66: 00 FEBC: ED FF12: ED FE11: D7 FE67: FE FEBD: 53 FF13: B0 FE12: F1 FE68: ED FEBE: FA FF14: CF FE13: E6 FE69: 5B FEBF: FD FF15: 08 FE14: 0F FE6A: FA FEC0: E5 FF16: 09 FE15: C6 FE6B: FD FEC1: 2A FF17: 2B FE16: 30 FE6C: A7 FEC2: 00 FF18: EB FE17: FE FE6D: ED FEC3: FE FF19: 09 FE18: 3A FE6E: 52 FEC4: ED FF1A: 2B FE19: 38 FE6F: 23 FEC5: 52 FF1B: EB FE1A: 02 FE70: 44 FEC6: E1 FF1C: ED FE1B: C6 FE71: 4D FEC7: 30 FF1D: B8 FE1C: 07 FE72: E1 FEC8: 04 FF1E: CF FE1D: D7 FE73: ED FEC9: ED FF1F: 08 FE1E: C9 FE74: 5B FECA: 53 FF20: CD FE1F: 2A FE75: 00 FECB: 00 FF21: 02 FE20: 00 FE76: FE FECC: FE FF22: FE FE21: FE FE77: 19 FECD: 0B FF23: 3E FE22: 22 FE78: 22 FECE: 78 FF24: 20 FE23: FC FE79: 00 FECF: B1 FF25: D7 FE24: FD FE7A: FE FED0: 20 FF26: 78 FE25: 54 FE7B: EB FED1: CA FF27: CD FE26: 5D FE7C: ED FED2: C9 FF28: 02 FE27: 2A FE7D: B8 FED3: 2A FF29: FE FE28: FA FE7E: 00 FED4: 00 FF2A: 3E FE29: FD FE7F: 00 FED5: FE FF2B: 20 FE2A: A7 FE80: 2A FED6: ED FF2C: 47 FE2B: ED FE81: 4B FED7: 5B FF2D: D7 FE2C: 52 FE82: 5C FED8: FC FF2E: 79 FE2D: 19 FE83: 23 FED9: FD FF2F: CD FE2E: D0 FE84: 4E FEDA: D5 FF30: 02 FE2F: 7C FE85: 23 FEDB: A7 FF31: FE FE30: CD FE86: 46 FEDC: ED FF32: 78 FE31: 02 FE87: CB FEDD: 52 FF33: D7 FE32: FE FE88: 28 FEDE: 44 FF34: 7A FE33: 7D FE89: CB FEDF: 4D FF35: CD FE34: CD FE8A: 19 FEE0: 03 FF36: 02 FE35: 02 FE8B: 28 FEE1: E1 FF37: FE FE36: FE FE8C: D5 FEE2: 23 FF38: 78 FE37: 3E FE8D: ED FEE3: ED FF39: D7 FE38: 20 FE8E: 5B FEE4: 5B FF3A: 7B FE39: D7 FE8F: FA FEE5: FA FF3B: CD FE3A: 7E FE90: FD FEE6: FD FF3C: 02 FE3B: CD FE91: 7A FEE7: ED FF3D: FE FE3C: 02 FE92: CD FEE8: B0 FF3E: 78 FE3D: FE FE93: 02 FEE9: 1B FF3F: D7 FE3E: 3E FE94: FE FEEA: 1B FF40: 7C FE3F: 20 FE95: 7B FEEB: ED FF41: CD FE40: D7 FE96: CD FEEC: 53 FF42: 02 FE41: 7E FE97: 02 FEED: 00 FF43: FE FE42: FE FE98: FE FEEE: FE FF44: 78 FE43: 20 FE99: 3E FEEF: C9 FF45: D7 FE44: 38 FE9A: 20 FEF0: 2A FF46: 7D FE45: 05 FE9B: D7 FEF1: FA FF47: CD FE46: FE FE9C: 23 FEF2: FD FF48: 02 FE47: A5 FE9D: 7E FEF3: 22 FF49: FE FE48: 30 FE9E: FE FEF4: F8 FF4A: 3E FE49: 01 FE9F: 40 FEF5: FD FF4B: 0D FE4A: D7 FEA0: 38 FEF6: 2B FF4C: D7 FE4B: 3E FEA1: 04 FEF7: 22 FF4D: E1 FE4C: 0D FEA2: E6 FEF8: 00 FF4E: 7C FE4D: D7 FEA3: DF FEF9: FE FF4F: CD FE4E: 23 FEA4: D6 FEFA: C9 FF50: 02 FE4F: 22 FEA5: 07 FEFB: ED FF51: FE FE50: FA FEA6: 87 FEFC: 5B FF52: 7D FE51: FD FEA7: 87 FEFD: FA FF53: CD FE52: 18 FEA8: 87 FEFE: FD FF54: 02 FE53: D6 FEA9: 87 FEFF: 2A FF55: FE FE54: 00 FEAA: D5 FF00: FC FF56: CF FE55: 2A FEAB: 57 FF01: FD FF57: 14 FE56: 4B FEAC: 23 FF02: A7 FE57: 5C FEAD: 7E FF03: ED FE58: 23 FEAE: FE FF04: 52 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - For advanced programmers ... Figures 2 and 3 give a machine code editing program called HEXLD3. You can load it into the computer using HEXLD as above. Its purpose is to allow you to construct and edit other programs in machine code. To avoid confusion the hex given in Fig.3 is called the "object program" - the machine code you will use it to edit is referred to as the "subject program". You should not attempt to use HEXLD3 to edit itself. If you are using a 16K machine instead of a 48K machine you must subtract 32768 from each address used in the BASIC, and you must change each address referred to in the machine code which begins with F into the corresponding address beginning with 7. The features of the program are as follows: - Fig.4 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - The arrangement of the variables and machine code routines of HEXLD3. 16K 48K --- --- 7DF8 FDF8 BEGIN Points to the first byte of the subject program. 7DFA FDFA ADDRESS ) 7DFC FDFC ADD2 )Parameters used by HEXLD3. 7DFE FDFE ADD3 ) 7E00 FE00 LIMIT Points to the first byte after the subject program. 7E02 FE02 HPRINT Subroutine to print the contents of the A register in hexadecimal. 7E1F FE1F HLIST List subject program in hexadecimal. 7E55 FE55 INSERT Inserts additional bytes into subject program. 7E80 FE80 WRITE Overwrites subject program with new code. 7ED3 FED3 DELETE Deletes bytes from the subject program. 7EF0 FEF0 BEGINMC Sets BEGIN and LIMIT ready for creating a new subject program. 7EFB FEFB HCOPY Overwrites subject program with bytes copied from elsewhere. 7F20 FF20 BREAKP Break point routine. 7F58 FF58 Next spare byte - user defined graphics may begin here. I am sorry if there is insufficient space to list the machine code for HEXLD3 in full. You may like to translate it for yourself as an exercise if you are sufficiently masochistic. RUN List machine code in hex from any hex address. RUN 100 Write your own machine code as in HEXLD above. RUN 200 Insert bytes of machine code between existing bytes. RUN 300 Delete bytes of machine code, closing up the gap which they occupied. RUN 400 SAVEs first the BASIC, then the object program, then the subject program, then verifies all three. RUN 500 Initially assigns variables used by this program. RUN 500 must be used only if you are creating a new program from scratch. RUN 600 Equivalent to DELETE followed by INSERT at the same address. RUN 700 RUN machine code from any address. RUN 800 Copy blocks of machine code from one address to another. FN H(string) Changes hex to decimal; eg. FN H("002A") = 42. FN H$ Changes decimal to hex; eg. FN H$(42) = "002A". (number) [sic. JG.] FN P (address) Equivalent to PEEK (address) + 256 * PEEK (address+1) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- Another Fine Product transcribed by: Jim Grimwood (jimg@globalnet.co.uk), Weardale, England --