MACHINE CODE TEST TOOL Tutor and Debug Program ZX Spectrum 16k & 48k Edition F O Ainley MACHINE CODE TEST TOOL for the 16k & 48k Spectrum Edition Two INTRODUCTION Welcome to this introduction to machine code. I have prepared this text to be used in conjunction with the Machine Code Test Tool program (from here on abbreviated and referred to as MCTT) in order to give you practical examples and "hands-on" experience ot using machine code. Using the MCTT you can enter and test your own machine code programs as well as examine the ZX SPECTRUM ROM and how your BASIC programs are stored in the RAM memory. Before beginning to read this book I strongly recommend that your read chapters 24 and 26 of your Sinclair ZX SPECTRUM Basic Programming book entitled "The memory" and "Using machine code", and Appendix E of the same book entitled "Binary and Hexadecimal". It was not my intention to write a complete treatise on the Zilog Z80 (the microprocessor used in the ZX SPECTRUM) but if, after completing the exercises in this book, you wish to go deeper into the subject I can recommend two books which I have found particularly useful. They are: Z80 Instruction Handbook Understanding your SPECTRUM Nat Wadsworth Dr. Ian Logan OCP Box 99 Oxford Melbourne House Publishers #6.00 Post Free LOADING If you have a 16k ZX SPECTRUM type LOAD "mctt16" ENTER or If you have a 48k ZX SPECTRUM type LOAD "mctt48" ENTER and load the program in the normal way. There is a 16k version and a 48k version of the program on each side of the cassette. On side 1 the 16k version is followed by the 48k version and on side 2 the 48k version is followed by the 16k version. When the program is loaded its title will appear for a short period, the screen will blank for a short period, then the normal K cursor or Sinclair (c) sign will appear. Please note that 16k program will not load on a 48k Spectrum or vice versa. If "mctt 16" appears first on your 16k machine this will load successfully. If "mctt 16" appears first and you have a 48k machine, flip the cassette, rewind and reload which will enter 48k program first. For CHARACTER GENERATOR: type LOAD "udg" ENTER and program will start automatically. To use the MCTT type: 1 LET A=USR 30592 ENTER if you have a 16k ZX SPECTRUM or 1 LET A=USR 63360 ENTER if you have a 48k ZX SPECTRUM From now on whenever you wish to run the MCTT just type: RUN ENTER and the MCTT > cursor will appear in the top left-hand corner of your T.V. screen. A complete list of MCTT commands is given in the Appendix. AN IMPORTANT POINT TO REMEMBER WHEN USING THE MCTT IS THAT TYPING ENTER AT ANY TIME WILL RETURN YOU TO THE COMMAND MODE. Thus, if you make a mistake, typing DELETE will have no effect and your only option is to type ENTER. I will begin with a brief explanation of the hexadecimal number system which explains the way the MCTT displays information contained within the computer's registers and memory. THE HEXADECIMAL NUMBER SYSTEM Our natural number system is decimal, ie. based on the number 10, because the physical equipment with which we have been endowed consists of 10 fingers. ThiS is our hardware. In this system 1 character can represent any one of 10 states, which we label 0, 1 ... or 9. The natural number system of a digital computer is based on 2 because its hardware consists of a series of electronic switches which can only register 1 of 2 states, which we label 0 or 1. Because it would be very inconvenient for us humans to represent values using only 0 and 1 we use the hexadecimal system which has 16 as its base, which is 2 to the power 4, ie 2x2x2x2. We would naturally count in the hexadecimal system if we had 16 fingers instead of 10. In that case, of course, we would need 6 extra characters to represent the 6 extra fingers. To accomplish this, A-F are used in the hexadecimal system to represent 10-15. We now have a number system which is based on 16 instead of 10. To represent any number between 0 and 15 we need use only one character: 0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F. But what about numbers greater than 15? The system works in exactly the same way as the decimal system: with 2 decimal characters we can represent any 1 of 100 (10x10) different states (00-99); with 3 decimal characters any 1 of 1000 (10x10x10) different states (000-999) and so on, ad infinitum. With 2 hexadecimal characters we can represent any 1 of 256 (16x16) different states (00-FF); with 3 hexadecimal characters any 1 of 4096 (16x16x16) different states (000-FFF); with 4 hexadecimal characters any 1 of 65536 (16x16x16x16) different states (0000-FFFF) and so on, ad infinitum. The MCTT contains a command (D) to convert any decimal number in the range 00000-65535 to its hexadecimal equivalent, and another command (H) to perform the reverse conversion, ie. any hexadecimal number in the range 0000-FFFF to its decimal equivalent. Let's see how this works by converting a decimal number to its hexadecimal equivalent. We will start by choosing the decimal value 200 (this value has no special significance but is just an example). |YOU TYPE|COMMENT |ZX DISPLAYS | | |> |D00200 | | | | |D00200=H00C8 | |C8 is the hexadecimal | | |equivalent of decimal | | |200 | Now let's convert a hexadecimal number to its decimal equivalent. In this example we will choose the hexadecimal value 1000. |YOU TYPE|COMMENT |ZX DISPLAYS | | |> |H1000 | | | | |H1000=D04096 | |4096 is the decimal | | |equivalent of hexadecimal | | |1000 | You should now experiment with these 2 commands in order to reach a better understanding of the hexadecimal number system. One word of warning: if you enter (using the D command) a decimal value greater than 65535, which is outside the conversion range of the command, then the words "=OVERFLOW" will appear on your T.V. screen - try it and see. From now on we shall follow every hexadecimal value with "h" to distinguish them from decimal values. Appendix A of your Sinclair ZX SPECTRUM Basic Programming book contains the first 256 decimal codes and their corresponding hexadecimal values. HOW A COMPUTER WORKS. To understand what machine code is, it is essential to have some idea of what the "machine", i.e. computer, is. Although we tend to think of anything connected with computers as being complicated because of the often complicated nature of the tasks most computers perform, we are lucky in that, conceptually, a computer is easy to understand. We can understand that concept more easily if we separate HOW a computer works from WHAT it does. We can understand how a computer works by taking just 3 elements. 1. A block of memory A memory block is divided up into units called BYTES. Each byte (unit) has 2 attributes: a) every byte is made up of 8 on/off switches, called BITS, and can contain any 1 of 256 (2 to the power of 8) codes (00-FFh); b) every byte has a unique numbered location which is called its ADDRESS. The addresses of the bytes start from 0 and rise sequentially by 1 to whatever the largest address permitted by a particular computer may be. For the Z80 this is 65535 (FFFFh) and for a large IBM machine perhaps 16,777,215 (FFFFFFh). 2. The program counter This is simply an amount of memory within the computer (the block of memory described above is external to the computer although of course connected to it), called a REGISTER, which contains an address of one of the locations described above. When the ZX SPECTRUM is turned on the program counter contains 0. All registers are either 1 or 2 bytes long. The program counter is 2 bytes long, sufficient to hold the address of any one of the ZX SPECTRUM's 65536 (0-65535) memory locations. 3. The execution unit This is the part of the computer that carries out instructions. Instructions alter registers and memory locations in very precisely defined ways which we shall discuss later. Each computer has its own very particular set of instructions which it will execute. Now that we have described the 3 elements we can see how they work together: 1. The program counter contains a memory address. The contents of the byte indicated by that address (which must be an instruction code, also called an operation code) are fetched to the execution unit. 2. The length of the instruction just fetched (instructions can be 1, 2, 3 or 4 bytes long) is added to the program counter, thereby forming the address of the next instruction to be executed. 3. The instruction is executed. The above cycle of instructions is repeated ad infinitum until the computer is switched off. This is all the computer ever does and in fact the power of the computer comes from its ability to repeat this fetch/execute cycle millions of times per second. One obvious point which may arise from the above description is "what happens when the program counter reaches the end of the memory?". The answer is that it must never be allowed to. This is achieved by making one of the instructions which is executed by the execution unit a "change program counter" instruction. This is effectively a "branch" or "goto" instruction. WHAT DOES A COMPUTER DO? We have already said that instructions alter registers and memory locations, and basically this is all that they do. We have already described one register, the program counter, and below is a diagram of the full internal register set of the Z80. .---.---. .---.---. | A | F | | A'| F'| | B | C | | B'| C'| | D | E | | D'| E'| | H | L | | H'| L'| .---.---. .---.---. | I X | | I Y | | S P | | P C | .---.---. | R | I | .---.---. To see how MCTT displays these registers: |YOU TYPE|COMMENT |ZX DISPLAYS | | |> |R | | | | |MAIN REGISTER SET | | |AF=xxxx BC=xxxx DE=xxxx HL=xxxx | | |FLAG S Z - H - P/V N C | | |BITS x x - x - x x x | | | | | |ALTERNATE REGISTER SET | | |AF=xxxx BC=xxxx DE=xxxx HL=xxxx | | |FLAG S Z - H - P/V N C | | |BITS x x - x - x x x | | | | | |SPECIAL PURPOSE REGISTERS | | |IX=xxxx IY=xxxx SP=xxxx PC=xxxx | | | I=xx R=xx | | |> Please note that I have used "x" above, and throughout this book, to denote a character which is unknown and/or not relevant to our purposes. However, the first time MCTT is entered the information contained in all these registers is "0" because this is a display of the information contained in the registers at the moment that a machine code routine of our own making has been interrupted, and we have not yet written any such routines - but we soon will. Some registers are 2 bytes long, always contain a memory address, and can only be used in 2 byte chunks. These are the IX, IY, SP, and PC registers (the PC register is the program counter I have already mentioned). The BC, DE, and HL register pairs can be used in either 1 or 2 byte quantities, as is shown in examples below. Thus, for example, the BC register pair can be accessed as BC, B or C. The A register, although paired with the F register, is mostly used on its own. The F register can only be used indirectly as we shall see later. (The information contained in the register display against the lines beginning "FLAG" and "BITS" is in fact purely a translation of the information contained in the F register, which we will also use later). The R and I registers are for special purposes and are of no interest to most programmers. Finally, there is a duplicate set of the AF, BC, DE, and HL register pairs, (listed in the register display under "ALTERNATE REGISTER SET"). The use of the HL register pair in this alternate set and of the IY register is not recommended as the ZX SPECTRUM control program, which is contained in the ROM and which controls many crucial operations of the computer, uses these registers as a temporary store. To fully explore the instruction set of the Z80, a book detailing the execution of each instruction is necessary, which is beyond the scope of this general introduction. However, we can now examine the operation of a number of common instructions to gain a broad understanding of the microprocessor. LOAD A REGISTER PAIR FROM MEMORY We will now enter a machine code instruction into memory at location 6000h and then execute it. The instruction is "load the BC register pair with the 2 byte value immediately following the operation (instruction) code". In this case we will choose the value 1122h. |YOU TYPE|COMMENT |ZX DISPLAYS | | |> |A6000 | | | |MCTT A (Alter) | | |command | | | |6000 xx > |01 |operation code | | | |6000 xx 01 | | |6001 xx > |22 |the value to go into C | | | |6001 xx 22 | | |6002 xx > |11 |the value to go into B | | | |6002 xx 11 | | |6003 xx > |ENTER | | | | |> |B6003 | | | |MCTT B (Breakpoint) | | |command | | | |B 6003 ? > |X | | | | |B 6003 ?X |G6000 | |> | |MCTT G (Goto) | | |command | | | |G 6000 ? > |X | | | | |BREAK AT 6003 | |Our instruction has |> | |now been executed | |R | | | |MCTT R (Register | | |display) command | | | |MAIN REGISTER SET | | |AF=xxxx BC=1122 DE=xxxx HL=xxxx | | |FLAG S Z - H - P/V N C | | |BITS x x - x - x x x | | | | | |ALTERNATE REGISTER SET | | |AF=xxxx BC=xxxx DE=xxxx HL=xxxx | | |FLAG S Z - H - P/V N C | | |BITS x x - x - x x x | | | | | |SPECIAL PURPOSE REGISTERS | | |IX=xxxx IY=xxxx SP=xxxx PC=6003 | | | I=xx R=xx | | |> At this point I would like to explain what we have just done. Using the A (Alter) MCTT command we entered the instruction into memory. Using the B (Breakpoint) MCTT command we inserted an instruction to jump to the MCTT when address 6003h was reached, ie. immediately after our ld bc,1122h instruction was executed. The G (Goto) MCTT command initiated execution of that instruction. The last command, R, displayed all of the Z80's registers after our instruction had been executed. As you can see, these registers now contain different values. These values represent the contents of the registers when the program counter reached address 6003h. From the register display we can see that the BC register pair now contains the value 1122h. We have just written our first machine code program! * A list of these instructions or operation codes is given in Appendix A of your Sinclair ZX SPECTRUM Basic Programming book. For example, in this case, under the column "HEX", you will find "01". Next to this, under the column "Z80 Assembler" you will find "ld bc,NN", which is the instruction we are just about to execute. LOAD A SINGLE REGISTER FROM MEMORY There are also instructions to alter individual registers. To demonstrate this we will enter and execute the instruction "load the B register with the 1 byte value immediately following the operation code". In this case we will choose the value 33h. |YOU TYPE|COMMENT |ZX DISPLAYS | | | |A6000 | | | | |6000 xx > |06 |operation code | | | |6000 xx 06 | | |6001 xx > |33 |value to go into B | | | |6001 xx 33 | | |6002 xx > |ENTER | | | | |> |B6002 | | | | |B 6002 ? > |X | | | | |B 6002 ?X | | |> |G6000 | | | | |G 6000 ? > |X | | | | |BREAK AT 6002 | | |> |R | | | | |MAIN REGISTER SET | | |AF=xxxx BC=3322 DE=xxxx HL=xxxx | | |FLAG S Z - H - P/V N C | | |BITS x x - x - x x x | | | | | |ALTERNATE REGISTER SET | | |AF=xxxx BC=xxxx DE=xxxx HL=xxxx | | |FLAG S Z - H - P/V N C | | |BITS x x - x - x x x | | | | | |SPECIAL PURPOSE REGISTERS | | |IX=xxxx IY=xxxx SP=xxxx PC=6002 | | | I=xx R=xx | | |> From the register display we can see that the B register (remember we are only altering directly the registers in the Main Register Set) now contains the value 33h while the C register retains the value 22h which we loaded into it with our ld bc,1122h instruction. ADD ONE REGISTER PAIR TO ANOTHER REGISTER PAIR The Z80 has various instructions to perform addition and subtraction but none to perform multiplication or division or other mathematical functions. If you want to do anything other than addition or subtraction in machine code I'm afraid you'll just have to write a program to do it yourself. To demonstrate an addition we will load the DE register pair with a value, load the HL register pair with a value, and add the DE register pair to the HL register pair, leaving the result in HL. In this case we will choose the values 1028h for HL and 2002h for DE ld hl,1028h put 1028h into HL; ld de,2002h put 2002h into DE; add hl,de add DE to HL An important point to bear in mind is that this is hexadecimal, not decimal, arithmetic. |YOU TYPE|COMMENT |ZX DISPLAYS | | |> |A6000 | | | | |6000 xx > |21 |the operation code | | |ld hl,NN | | | |6000 xx 21 | | |6001 xx > |28 |the value to go into L | | | |6001 xx 28 | | |6002 xx > |10 |the value to go into H | | | |6002 xx 10 | | |6003 xx > |11 |the operation code | | |ld de,NN | | | |6003 xx 11 | | |6004 xx > |02 |the value to go into E | | | |6004 xx 02 | | |6005 xx > |20 |the value to go into D | | | |6005 xx 20 | | |6006 xx > |19 |the operation code | | |add hl,de | | | |6006 xx 19 | | |6007 xx > |ENTER | | | | |> |B6007 | | | | |B 6007 ? > |X | | | | |B 6007 ?X | | |> |G6000 | | | | |G 6000 ? > |X | | | | |BREAK AT 6007 | | |> |R | | | | |MAIN REGISTER SET | | |AF=xxxx BC=xxxx DE=2002 HL=302A | | |FLAG S Z - H - P/V N C | | |BITS x x - x - x x x | | | | | |ALTERNATE REGISTER SET | | |AF=xxxx BC=xxxx DE=xxxx HL=xxxx | | |FLAG S Z - H - P/V N C | | |BITS x x - x - x x x | | | | | |SPECIAL PURPOSE REGISTERS | | |IX=xxxx IY=xxxx SP=xxxx PC=6007 | | | I=xx R=xx | | |> From the register display we can see that DE now contains the value 2002h we loaded into it, while HL now contains 302Ah, which is the result of the hexadecimal addition of 2002h and 1028h. COMPARE AND BRANCH Now we come to a very important set of instructions - the compare and branch instructions. In order to be able to make decisions the computer must have a mechanism for determining if certain conditions have occurred and how to communicate the fact that those conditions have occurred to other instructions. The mechanism to accomplish this is very simple and involves a register set aside specifically for this purpose. This register is the F (flag) register, so called because its function is to flag, or signal to other instructions the results of preceding instructions. The flag register has 8 bits, the same as any other byte, and 6 of these bits are used as 6 separate flags, called the S,Z,H,P/V,N and C flags. When you use the MCTT register display command "R", the hexadecimal value of the flag register is shown, together with the setting of each bit of the flag register (next to the words "FLAG" and "BITS"). (When we talk about bit settings we usually refer to a bit being "set" if = 1, otherwise "reset" if = 0). To demonstrate their use we shall concentrate on one flag, the Z (zero) flag. Most compare instructions use the A register for one half of the comparison. The operation of a compare between, for example, the A and B registers works like this: the computer performs an imaginary subtraction of the B register from the A register, leaving both registers unaltered, and if the result of that imaginary subtraction would have resulted in the A register containing 0, it sets the Z flag. In all other cases the Z flag is reset; ie. if A>B or A | | |6000 |3E |operation code | | |ld a,N | | | |6000 xx 3E | | |6001 xx > |01 |value to go into A | | | |6001 xx 01 | | |6002 xx > |06 |operation code | | |ld b,N | | | |6002 xx 06 | | |6003 xx > |01 |value to go into B | | | |6003 xx 01 | | |6004 xx > |B8 |operation code | | |cp b | | | |6004 xx B8 | | |6005 xx > |CA |operation code | | |jp z,NN | | | |6005 xx CA | | |6006 xx > |0A |second byte of the | | |branch address | | | |6006 xx 0A | | |6007 xx > |60 |first byte of the | | |branch address | | | |6007 xx 60 | | |6008 xx > |3E |operation code | | |ld a,N | | | |6008 xx 3E | | |6009 xx > |FF |value to go into A | | | |6009 xx FF | | |600A xx > |ENTER | | | | |> |B600A | | | | |B 600A ? > |X | | | | |B 600A ?X > |G6000 | | | | |G 6000 ? > |X | | | | |BREAK AT 600A | | |> |R | | | | |MAIN REGISTER SET | | |AF=0142 BC=xxxx DE=xxxx HL=xxxx | | |FLAG S Z - H - P/V N C | | |BITS 0 1 - 0 - 0 1 0 | | | | | |ALTERNATE REGISTER SET | | |AF=xxxx BC=xxxx DE=xxxx HL=xxxx | | |FLAG S Z - H - P/V N C | | |BITS x x - x - x x x | | | | | |SPECIAL PURPOSE REGISTERS | | |IX=xxxx IY=xxxx SP=xxxx PC=600A | | | I=xx R=xx | | |> From the register display we can see that the Z flag is set; ie. = 1, and that the A register now contains the value 01h we loaded into it. This means that the branch instruction skipped around the ld a,FFh instruction before stopping at location 600Ah because the contents of the A register were equal to the contents of the B register. If we change the value that is loaded into B as follows, we can see the result: |YOU TYPE|COMMENT |ZX DISPLAYS | | |> |A6003 | | | | |6003 01 > |02 |change the value | | |loaded into B | | | |6003 xx 02 > | | |6004 xx > |ENTER | | | | |> |G6000 | | | | |G 6000 ? > |X | | | | |BREAK AT 600A | | |> |R | | | | |MAIN REGISTER SET | | |AF=FF93 BC=02xx DE=xxxx HL=xxxx | | |FLAG S Z - H - P/V N C | | |BITS 1 0 - 1 - 0 1 1 | | | | | |ALTERNATE REGISTER SET | | |AF=xxxx BC=xxxx DE=xxxx HL=xxxx | | |FLAG S Z - H - P/V N C | | |BITS x x - x - x x x | | | | | |SPECIAL PURPOSE REGISTERS | | |IX=xxxx IY=xxxx SP=xxxx PC=600A | | | I=xx R=xx | | |> From this we can see that the Z flag is reset to 0 and therefore the instruction to skip around the ld a,FFh instruction has not been executed and as a result the A register now contains FFh. It is important to realise that only some instructions affect the flag register and in this last example the last instruction to be executed, ld a,FFh, did not affect the flag register in any way. We have just seen the operation of a conditional branch instruction. Of course branch instructions do not have to be conditional and unconditional branch instructions do exist in the Z80 instruction set and are frequently used. CALL & RETURN A very special version of the jump instruction - call - is one of the most widely used machine code instructions. The idea behind the call is this: the execution of one group of instructions may be temporarily interrupted to execute another group of instructions, generally referred to as a subroutine. The mechanism to accomplish this also involves a register set aside specifically for this purpose. This register is the SP (stack pointer) register. This is simply a register which points to an area of memory, called the stack, which is used to store the address following the call instruction (the return address). When a call is executed the current contents of the program counter (the return address) are moved to the address pointed to by the stack pointer, and the stack pointer is decremented by 2. The address contained in the call instruction is moved to the program counter and thus the instruction at that address becomes the next instruction to be executed. When it is desired to return to the instruction following the call instruction, a return instruction is executed. This simply reverses the above process: the contents of the address pointed to by the stack pointer are moved back to the program counter and the stack pointer is incremented by 2. It is obviously essential that the contents of the stack pointer are not changed in between these operations - a common programming error. The call instruction is best illustrated using the following diagram. When the call 7000h instruction is executed, control passes to address 7000h and instruction A at that address becomes the next instruction to be executed. When the ret (return) instruction is executed, control passes back to address 6003h and execution of the original routine is resumed with the execution of instruction B. In this way self-contained routines may be written to perform specific tasks, thus easing program development and making possible the logical structuring of large programs. 6000h CALL 7000h ---. .---> 6003h instr B | | * * * | | * * * | | .--------------------------------. | | | .---> 7000h instr A subroutine starts here | * * * | * * * | * * * .-------------- RET To demonstrate this we can enter and execute the following program: address 6000h call 7000h go to address 7000h; 6003h 7000h pop hl put last 2 bytes of stack into HL; 7001h push hl reset stack pointer; 7002h ret return to address 6003h |YOU TYPE|COMMENT |ZX DISPLAYS | | |> |A6000 | | | | |6000 xx > |CD |the operation code | | |call | | | |6000 xx CD | | |6001 xx > |00 |the second byte of the | | |call address | | | |6001 xx 00 | | |6002 xx > |70 |the first byte of the | | |call address | | | |6002 xx 70 | | |6003 xx > |ENTER | | | | |> |A7000 | | | | |7000 xx > |E1 |the operation code | | |pop hl | | | |7000 xx E1 | | |7001 xx > |E5 |the operation code | | |push hl | | | |7001 xx E5 | | |7002 xx > |C9 |the operation code | | |ret | | | |7002 xx C9 | | |7003 xx > |ENTER | | | | |> |B7002 | | | | |B 7002 ? > |X | | | | |B 7002 ?X > |G6000 | | | | |G 6000 ? > |X | | | | |BREAK AT 7002 | | |> |R | | | | |MAIN REGISTER SET | | |AF=xxxx BC=xxxx DE=xxxx HL=6003 | | |FLAG S Z - H - P/V N C | | |BITS x x - x - x x x | | | | | |ALTERNATE REGISTER SET | | |AF=xxxx BC=xxxx DE=xxxx HL=xxxx | | |FLAG S Z - H - P/V N C | | |BITS x x - x - x x x | | | | | |SPECIAL PURPOSE REGISTERS | | |IX=xxxx IY=xxxx SP=xxxx PC=7002 | | | I=xx R=xx | | |> From this register display we can see that the HL register pair now contains the return address (pop hl put the contents of the stack into HL and decremented the stack pointer by 2 - push hl incremented the stack pointer by 2 to restore the status quo). If we examine the contents of the stack by taking the value of the SP register, we shall see that the first 2 bytes on the stack are the return address 6003h in REVERSE order. (In reverse order as all addresses used by the Z80 stored in memory are in reverse order. Eg. if the contents of these 2 bytes are 0360h the actual address is 6003h. To examine the stack, note the value of the SP register given under the "SPECIAL PURPOSE REGISTERS" heading. You will use this address where xxxx is indicated below. E.g. if the SP contained 7764h you must type P7764. |YOU TYPE|COMMENT |ZX DISPLAYS | | |> |Pxxxx | | | | |a hexadecimal page If we remove the breakpoint at 7002h and execute the call at 6000h again, we shall see that the execution sequence will be halted at address 6003h - showing that the subroutine at 7000h has been executed and control has returned to address 6003h. |YOU TYPE|COMMENT |ZX DISPLAYS | | |> |N | | | | |N 7002 | | |> |G6000 | | | | |G 6000 ? > |X | | | | |BREAK AT 6003 | | |> LOAD A MEMORY LOCATION FROM REGISTER We have already seen how to load a register and register pair from the memory. We can also do the reverse of course, ie. load a memory location from a register. Part of the memory is used by the ZX SPECTRUM to store the information that appears on your T.V. screen, so if we change that memory we should be able to see the results immediately. The display memory begins at address 4000h and is in two parts. The first part has a bit set aside for each dot that appears on the T.V. screen, the maximum number of bits being 256*192 which is 49152 bits or 6144 (1800h) bytes (49152/8). This portion of the display memory occupies addresses 4000h to 57FFh. The second part of the display memory controls the attributes of the 768 (32*24) blocks that make up the display and is 768 (300h) bytes long. Each byte controls the flashing, brightness, foreground and background colour attributes of each of the blocks. This portion of the display memory occupies addresses 5800h to 5AFFh. To get an idea of how the information contained in the display memory relates to what appears on the T.V. screen, try altering the values contained in the above locations using the MCTT A (Alter) command. At this point you might like to re-read chapter 24 of your Sinclair ZX SPECTRUM BASIC programming book entitled "The memory" for additional information. We can demonstrate the use of the display memory by writing a short program to completely till each screen dot position. address 6000h ld bc,1800h put length of display memory into BC (this will act as a counter for the number of times we are going to move FFh to the display memory); 6003h ld hl,4000h put start address of display memory into HL; 6006h ld a,FF load FFh into A; 6008h Id (hl),a load the contents of A into the memory location whose address is contained in HL; 6009h inc hl point HL to next memory location; 600Ah dec bc decrement move counter; 600Bh ld a,b if either B or 600Ch or c C are not=0 then 600Dh jp nz,6006h repeat the instructions at address 6006h. |YOU TYPE|COMMENT |ZX DISPLAYS | | |> |A6000 | | | | |6000 xx > |01 |the operation code | | |ld bc,NN | | | |6000 xx 01 | | |6001 xx > |00 |the value to go into C | | | |6001 xx 00 | | |6002 xx > |18 |the value to go into B | | | |6002 xx 18 | | |6003 xx > |21 |the operation code | | |ld hl,NN | | | |6003 xx 21 | | |6004 xx > |00 |the second byte of | | |address 4000h | | | |6004 xx 00 | | |6005 xx > |40 |the first byte of | | |address 4000h | | | |6005 xx 40 | | |6006 xx > |3E |the operation code | | |ld a,N | | | |6006 xx 3E | | |6007 xx > |FF |the value to go into A | | | |6007 xx FF | | |6008 xx > |77 |the operation code | | |ld (hl),a | | | |6008 xx 77 | | |6009 xx > |23 |the operation code | | |inc hl | | | |6009 xx 23 | | |600A xx > |0B |the operation code | | |dec bc | | | |600A xx 0B | | |600B xx > |78 |the operation code | | |ld a,b | | | |600B xx 78 | | |600C xx > |B1 |the operation code | | |or c | | | |600C xx B1 | | |600D xx > |C2 |the operation code | | |jp nz,NN | | | |600D xx C2 | | |600E xx > |06 |the second byte of | | |address 6006h | | | |600E xx 06 | | |600F xx > |60 |the first byte of | | |address 6006h | | | |600F xx 60 | | |6010 xx > |ENTER | | | | |> |B6010 | | | | |B 6010 ? > |X | | | | |B 6010 ?X | | |> |G6000 | | | | |G 6000 ? > |X | | | | |a completely black page then | | |BREAK AT 6010 | | |> |YOU TYPE|COMMENT |ZX DISPLAYS | | |> |M0000 |17FF 4000 | | | |M 0000 17FF 4000 ? |X | | | | |a random bit pattern |ENTER | | | | |> EPILOGUE As I said in the introduction, should you wish to proceed further you will almost certainly need additional literature to enable you to reach a fuller understanding of the Z80 and its operation within the framework of the ZX SPECTRUM. You have, however, the MCTT, which is a powerful tool in machine code program development and one with which you should become well acquainted in order to simplify that development. In developing machine code programs you might find the following tips of some use: 1) Write your machine code program down on paper first and try to resolve any problems at that stage before typing it into the memory. 2) Don't be afraid to experiment; your program may crash and force you to reload the MCTT but you can't physically damage the ZX SPECTRUM with software. 3) Try to imagine what is happening in the registers and memory, and what sequence the instructions are being executed in when things don't work out as expected; very often you will find that what you expect to happen is not what is actually happening. 4) Try to isolate the problem; use of the B (breakpoint) MCTT command enables you to interrupt your machine code programs at pre-specified points to ensure that any intermediate results are correct. 5) The computer is an absolutely obedient servant; if you make a mistake and force it to perform nonsense that is exactly what it will do. It is your task to impose order and logic on its sequence of operations. If you write in machine code there is no safety net in the shape of a BASIC interpreter to bail you out if things go wrong. I hope that this introduction to machine code programming has been instructive and helped you to a clearer understanding of what machine code programming is all about. In trying to resolve seemingly intractable problems I find it very helpful to keep in mind the old IBM motto, THINK. Good luck! APPENDIX The Machine Code Test Tool (MCTT) is a machine code program designed to run on the Sinclair ZX SPECTRUM with a 16k or 48k RAM. The 16k version occupies addresses 7780h to 7FFFh; the 48k version occupies addresses F780h to FFFFh. On loading, RAMTOP is set to 7880h or F780h respectively. The program is supplied on a cassette containing four copies of the program, a 16k and 48k version on both sides. The MCTT will allow you to easily enter machine code instructions and to test these instructions in operation. Should you wish to save any machine code programs you write, you should enter them into dummy REM statements and use the BASIC SAVE statement to save them on cassette in the normal way. Loading the MCTT To load the MCTT type LOAD "mctt16" ENTER if you have a 16k ZX SPECTRUM or LOAD "mctt48" ENTER if you have a 48k ZX SPECTRUM and load the program in the normal way. There is a 16k version and a 48k version of the program on each side of the cassette. On side 1 the 16k version is followed by the 48k version and on side 2 the 48k version is followed by the 16k version. When the program is loaded its title will appear for a short period, the screen will blank for a short period, then the normal K cursor will appear. To then use MCTT type: 1 LET A=USR 30592 ENTER if you have a 16k ZX SPECTRUM or 1 LET A=USR 63360 ENTER if you have a 48k ZX SPECTRUM From now on whenever you wish to run the MCTT just type: RUN ENTER and the MCTT > cursor will appear in the top left-hand corner of your T.V. screen. COMMANDS A - Alter memory. A aaaa This command allows you to alter any RAM byte to any hexadecimal character desired. At the end of a page this routine will only accept X, to continue with the next page, or ENTER to return to the command mode. B - Breakpoint. B aaaa?X This command allows you to insert a breakpoint in any piece of machine code, in RAM. The following points should be kept in mind when inserting breakpoints: Breakpointing works by inserting a call instruction (which is 3 bytes long) to a routine within MCTT at the address where a breakpoint is desired. This address must obviously be the first byte of an instruction - it is not possible to put a breakpoint in the middle of an instruction. For this reason also, no two breakpoints should be within less than 3 bytes of each other. When a breakpoint is reached the message "BREAK AT AAAA" is displayed. D - Decimal to Hexadecimal conversion. D ddddd = HHHH This command converts any decimal value in the range 00000-65535 to its hexadecimal equivalent. If a value > 65535 is typed in then the words "= OVERFLOW" will appear. G - Go to address. G aaaa?X This command will allow you to begin executing any machine code instructions at the address given. H - Hexadecimal to decimal conversion. H hhhh = DDDDD This command converts any hexadecimal value in the range 0000-FFFFh to its decimal equivalent. M - Moves memory block. M aaaa aaaa aaaa This command moves a block of memory to a RAM location. The parameters of this command are three addresses. Address 1 is the address of the start of the block, address 2 is the address of the end of the block. If this address is less than address 1, ie. an error, no further input for this command will be accepted and a new command must be typed in. Address 3 is the address of the location where the block will be moved to. N - Nullify breakpoint. N This command nullifies the LAST breakpoint inserted and restores the instructions contained at that address. Each time a breakpoint is nullified, the address of the breakpoint is displayed. You can have as many breakpoints active at one time as you like, but if you insert more than 1 breakpoint, the 3 byte call to the routine within MCTT discussed above, can never be replaced by your original instructions. In that case you will have to manually re-enter those instructions using the A (Alter) command. P - Page memory display. P aaaa This command will display a page of memory. After displaying a page you can display the next page by typing "X". R - Register display. R All registers saved at the execution of a breakpoint are displayed. The 2 flag registers are displayed in hexadecimal and bit form. If it is desired to alter the contents of the first four register pairs then the A (Alter) command should be used to alter the memory locations where they are stored and loaded from when the G (Goto) command is executed. These addresses are 793Ch to 7943h in the 16k version, and F93CH to F943h in the 48k version. S - Stop. S This command will return you to BASIC. V - View breakpoint. V AAAA This command will display the last breakpoint you entered. GLOSSARY aaaa - 4 character (2 byte) hexadecimal address supplied by you. ddddd - 5 decimal characters supplied by you. hhhh - 4 hexadecimal characters supplied by you. AAAA - 4 byte hexadecimal address supplied by MCTT. DDDDD - 5 decimal characters supplied by MCTT. HHHH - 4 hexadecimal characters supplied by MCTT. X - Confirmation character that the command entered is correct. No other characters besides X and ENTER (to cancel the command) will be accepted. ? - Prompt character displayed by MCTT to solicit the confirmation character "X". ENTER typed at any time will return the control to command mode. Copyright 1982 F.O. Ainley. Design: Scott/Kemp Design Ltd. Produced by Artset Graphics, The Old British School House, East Street, Chesham, Buckinghamshire. Tel. (0494) 771938. OTHER ZX PROGRAMS FROM OCP. 1. FULL SCREEN EDITOR/ the latest and most comprehensive Editor/ ASSEMBLER Assembler yet produced. FULL SCREEN 42 (16 & 48k) column input, text editor, assemble to tape, screen or memory, assemble derivatives DEFM, DEFS, DEFW, DEFB, DEFL, ORG, EQU, END. Comprehensive syntax check, binary, octal, HEX and ASCII constants. Superb fully notated "SNAKE" demonstration program as well as our 42-page instruction manual complete this essential piece of software. This program is co-resident with the MACHINE CODE TEST TOOL; the two programs create a powerful and complete machine code programming environment #9.95 2. MASTER TOOL KIT: add many new commands and facilities to your (16 & 48k) ZX Spectrum with this remarkable program. Re-Number, Auto-Number, Delete/Copy and Move Block, String Search and Substitute, Variable Dump, Cross Reference, Trace Function, Real Time Clock and Alarm, Operating Program Line Display plus many more features. Programming will never be the same after you have purchased MASTER TOOL KIT. #9.95 3. CHESS THE TURK: a very comprehensive chess program for the 48k (48k) Spectrum #8.95 4. ADDRESS MANAGER: a fast machine-coded application program that (48k) Limited on 16k offers Spectrum owners a professional standard address/data filing, indexing and retrieval system. Will store up to 400 full name and addresses in 48k, full screen entry is standard. #8.95 5. FINANCE MANAGER: a powerful and flexible MENU DRIVEN program (48k) program for practically all domestic and business accounting applications. Features include AUTOMATIC DOUBLE ENTRY, ANALYSIS, STANDING ORDERS, RECONCILIATION and FULL SCREEN ENTRY. Available for 48k only. #8.95 80 COLUMN PRINTOUTS Items 1, 4 & 5 can be supplied as Plus 80 versions and used in conjunction with a Centronics Interface. They include software to call up most Centronic printer routines. Price #19.95. A V.A.T. Manager in both editions will be available soon.