MACHINE CODE TRACE by Carol Brooksbank from ZX Computing Feb/Mar.1986 Coventry's Carol Brooksbank wrote this utility to find bugs in her programs and she thought she'd share it with us. I don't know about you, but I don't think that I have ever written a machine code program which ran perfectly first time. You know the feeling. Eagerly, you type in your latest masterpiece, enter RANDOMIZE USR something-or-other - and CRASH! There you sit, with a frozen keyboard and only a blank screen or a pretty psychedelic pattern to look at. You have no idea whether there is a fault in the logic of your program, whether you have made a typing mistake or miscounted a displacement, and you don't know where to start looking for the trouble, because you do not know how far into the program the crash occurred. Well, help is at hand. This machine code program will give you a hex trace at the right of the screen, as your program runs. It is only a partial trace, as it does not show the address of every instruction as it is executed - if it did, the display would change so fast that you would never be able to read it - but every 1/50 second it gives you the address that the program has reached. This is enough to let you keep an eye on the progress of your program, and to see where things start to go wrong. For instance, if the crash is caused by the program getting into an endless loop, you will see the same sequence of bytes repeated over and over again after the crash happens. If you left out a return instruction, so that the program starts running through the empty bytes above your program, that too will be obvious. But remember that the trouble is not always at the point where the crash happens. A wrong displacement instruction may be some way away from the point to which it directs the program. You will still have to think for yourself to decide why the program runs as it does. Why is the display in hex? Two reasons. The first is purely personal. I wrote the program for myself in the first place, and I always work in hex, so a decimal display would not be very helpful. (One of these days I shall find myself asking the greengrocer for "0A pounds of potatoes, please".) The second reason is rather more important. There is a direct relationship between the binary form of a number - the bit pattern held in the registers - and the hex form, which makes the conversion between the two very straightforward. Converting an address to decimal would involve multiplying the high byte by 256, adding the low byte, then isolating the 5 digits one by one for printing, all of which would make the routine much more complicated. Since the trace routine is in the form of an interrupt subroutine, it is desirable that it should be as short and simple as possible. The routine makes use of the fact that, whenever the Spectrum performs a subroutine, the return address is pushed onto the stack. On an interrupt subroutine, the return address is the program counter, the point reached in the main program. If we can retrieve this address from the stack and display it, we have a trace. Obviously, there are a lot of instructions performed in between the interrupts which are not displayed, but this is usually enough to let you see where a program takes a wrong turning. So, if your machine code program crashes, load in this routine with your own program - I am assuming that you always take the precaution of taping your programs before running them, just in case - enter RANDOMIZE USR 65271 (48K), 32503 (16K(, run your program again, and all should be revealed. Details The program is explained by the notes in the listing, but there are one or two details which need more explanation. The interrupt subroutine starts by saving the present value of HL in the two spare bytes in the system variables area at 5C80. This is necessary because the existing values of registers must always be saved at the start of an interrupt subroutine, and if we push it onto the stack, it will cover up the address we are trying to retrieve. The address is then POPped from the stack in HL, PUSHed back again so that it is in its correct place when the return is made from the subroutine, and the other register values can then be saved on the stack. The other spare byte among the system variables, 5C81, is used as an interrupt counter. If this has reached 22d, the printing position is set to the top of the screen and the counter reset to 0. Otherwise, the routine jumps forward to print the address. The print subroutine starts with the instruction AND 0F, which has the effect of resetting bits 4-7 of the A register, leaving bits 0-3 unchanged, isolating the number we wish to print. PRINT must be called, therefore, with the number to be printed in bits 0-3 of A. If the number to be printed is the "left-hand" digit of the two in the A register, the instruction RRA is performed 4 times, to move it to the "right-hand" position, but the print subroutine is called directly when the "right-hand" digit is to be printed. When PRINT is called, the DE register holds the first byte of the screen position for the digit, and at the end of the PRINT subroutine, DE is restored to that position. Since there are only 16 digits which we shall need to print, 0-9 and A-F, a table is set up, starting at FED7 (7ED7 16K), which holds the start addresses of the bit patterns of those digits in the ROM character table. Doubling the value of the number to print and adding it to the address of our table, points to the correct place in the table to retrieve the ROM address for that character. The digit can then be printed. After the 4 digits have been printed, the program variable SCRP at FF13 (7F13) is pointed to the next screen row down, and the program exits via the normal interrupt subroutine. The listing is for the 48K machine. 16K folk should change the initial "F" in the addresses to "7", each CALL PRINT instruction should read CDB97E, and the bytes at 7EBD, which point to HL to the start of the table should be 21D77E. At START, the high byte of the interrupt vector address should be 28, giving the bytes 3E28. The interrupt vector address is not required at 7EFF, so the four bytes between 7EFD and 7F01 may be changed to NOP if you wish, though if they are left as they are the program will simply ignore them. Saving To SAVE the routine on tape: SAVE "m/c trace" CODE 65116,184 (48K) SAVE "M/c trace" CODE 32348,184 (16K) [NOTE: There are actually 185 bytes in the listing, although the last] [two are just used as temporary storage. JimG] To START the trace: RANDOMIZE USR 65271 (48K) RANDOMIZE USR 32503 (16K) To STOP the trace: RANDOMIZE USR 65292 (48K) RANDOMIZE USR 32524 (16K) Finally, remember that the trace will not work if the interrupts are disabled. You must change your DI and EI instructions to NOP while using the trace, and restore them when you have corrected your problems. -- end of file --