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 --

