.cj David's Tracer Instructions by David A. Wheeler (C) 1985 David A. Wheeler; lightly revised on 2010-06-25 .lj .cj Introduction .li These are instructions for "David's Tracer", a 6502 (8-bit) machine-level tracing program for the the Apple ][. If you write machine language programs for an Apple ][, it's really useful. .lj The most important question is what's a tracer and why is this one the best one?? Well, if people could learn assembly language instantly and programmers never made mistakes, tracers would never exist. Unfortunately, assembly language can sometimes get confusing for those new to it, and when a programmer makes an error in an assembly program it can take hours to find it! A tracer is a tool which simply imitates the computer, executing programs one instruction at a time, but with a difference- the programmer can watch what is happening on each instruction. This way, neophytes and experts alike can see exactly why their program is going out to lunch and save themselves hours of trouble. .cj So what's so good about this tracer?? .lj A tracer is only useful if it can trace the program with problems without changing the program. This tracer was designed to fool the program it is tracing much better than other tracers. It keeps a copy of a very important area, called the zero page, so that the tracer will keep tracing if this area is changed. This tracer can omit displaying areas of code if it is told to do so (this means you don't have to look at stuff which you already know is okay). It can also "JSR" areas of code, so that programs that use input and output can be traced. The program normally works with areas of computer memory, while all other tracers only deal with one or two spots in it. Almost all of this happens without you having to worry about it. Hopefully this has whetted your appetite- the whole idea of the tracer is to show you problems so you have do as little work as necessary! .cj How to get the tracer started .lj There are 4 basic steps for using the tracer. Each will be explained in more detail below. 1. In order to start the tracer up, simply type from any Basic or the monitor the following command: BRUN TRACER After you do this, the tracer will be available. 2. Then load in whatever machine language program you wish to trace. 3. To actually start the tracer up, you need to be using the monitor. If you are using Basic, type CALL -151 which will put you in the monitor. The monitor displays an asterisk at the left. 4. To start the tracer up, type the ADDRESS of the program where you wish to begin tracing. The tracer will display the first line of the program and what effect that command has on various registers. You will be in what is called the STEP mode- each time you press a space or RETURN you will execute one computer (machine) instruction. .cj Overall commands from accessing the tracer .lj From Basic, a BRUN TRACER will load the tracer into memory and activate it. From Basic, a CALL -151 will switch from Basic to the monitor (showing the asterisk symbol, *). From the monitor, the command: address will start tracing at that address, putting you in "step mode". Pressing ESC from the "step mode" will bring you back to the monitor. Typing the monitor command 3D0G will return you to Basic. .cj Step commands .lj The following commands are available while you are in step mode: T - continuous trace. Press any key to return to step mode. P - program history. The last 128 steps are displayed, latest instruction executed first. V - Victim display (make display the program being traced) N - Normal display (show tracing output) controlC - stop invisible run ESC - Escape. Leaves the tracer and returns to the monitor. .cj Special Tracer commands .lj These commands must be executed from the monitor and after the tracer is BRUN. Each command is entered in the following way: For example, the List command is entered by typing , the L key, and then (3 characters). .cj Immediately-executed commands .lj L List all stored area commands. N New. Eliminate all stored commands. U Undo. Undo the last stored area command. C Case change. Changes the case of the off flags in the trace. P Program history. Displays the last 128 instructions. null If you just type control-Y by itself and press RETURN, the tracer will restart tracing wherever it stopped. .cj Stored Instructions .lj These instructions are stored in a list. To see this list use the L command (see above). You may use up to 50 area commands, but each one will slow the tracer down slightly. Stored commands are also entered from the monitor in the following way: starting address.ending address control-Y commandletter RETURN If the ending address is the same as the starting address, omit the dot and the ending address. For example, to JSR to any monitor subroutine call, type in: F800.FFFF control-Y J (don't type the spaces!) To Break at line $1005: 1005 control-Y B I Invisible area. Trace through this area but don't display the instructions. Automatically traces through many instructions. B Break. Any time an instruction is executed in this area the program goes to step mode if it was in continuous mode. J Jsr. Any time an instruction is executed in this area the tracer JSRs that program itself, assuming that this program is a subroutine. If the program there doesn't return, then the tracer never returns either. Also, if the program expects data to follow the call to the program (like Sweet-16 and PRIM) then this option will fail- use I for these kinds of programs instead. The tracer can fool programs which call to a JSR area and then find out their own address. You MUST use this option when calling timing-sensative routines (like disk operations) or they WON'T work! It is recommended that almost all DOS or monitor calls are J'd.. they already work, no reason to check them carefully! J CANNOT be combined with other commands, because the JSR is executed immediately. A JSR is indicated on the trace by the address and a J after it indicating a JSR took place. Y Yell. Generates noise on a sep. F Flip. Turn on/off dual screen display. G Guarantee. This range of code doesn't use/change display. Use this on program areas that don't change the display to speed up execution. .cj Registers .lj If you wish to set the registers to certain values before you start executing a program, go to the monitor and type control-E This will show the values of the registers will be when the trace starts. To change, just type : value value value ... Which will change the values of the A, X, Y, P, and S registers in that order. .cj Zero Page .lj Zero page is one of the most important places in a 6502-based machine like the Apple. Whenever the tracer is engaged (BRUN or a monitor G to its beginning) it saves the zero page in a location $100 higher than the tracer's beginning. This means from then on if you wish you change the victim's zero page, you change tracer's address + $100 + address you wish to change. This allows windowing, a subject so helpful it is covered as a seperate subject below. .cj We do windows! .lj Because the victim has its own zero page, windowing is very easy to do with David's Tracer. Here's how to do it: 1. Set up your victim's window. A good way is to get to the monitor and type in.. 22:0 9 @ This sets the window top to line 0 (the first line), the bottom to line 9 and clears the screen (making sure you get there). 2. Activate the tracer- this saves the zero page. From the monitor just type in the starting address, a G, and then a return. For example, if the starting address is $4000, type 4000G @ 3. Set up your tracer's window. Continuing this example, typing in 22:0A 18 @ Would set the top to line $A (line 10), the bottom to line $18 (line 23), and clears the screen again. From now on when you trace your victim will appear on the top and the tracer below, at least unless your victim demands the full screen. The normal value in $22 (top line) is 0 (line 0), and the normal value in $23 (bottom line) is $18 (line 23). You can set this by typing: 22:0 18 from the monitor. .cj Other comments .lj I find a 9600.FFFF control-Y J very helpful- that means any DOS, monitor, Basic, or I/O call gets JSR'ed, guaranteeing a quicker trace for most programs and keeping you from tracing working programs. Putting an area break on areas outside the program will halt the program if it goes to nowhereville- then just press P and see how it got there. Temporarily disengaging DOS speeds up the trace. You can disengage DOS by typing in 0 control-K 0 control-P from the monitor. Don't forget Invisible- it comes in handy. .cj Notes for the advanced programmer .lj David's Tracer is designed to be as aloof as possible from whatever its victim does. However, knowing what it does can really help. The tracer's stack is $60 off from where the victim's stack is. If the victim's stack changes a lot, or the victim writes onto the stack area (don't do that!) then you may want to change the stack position before you call the program. The tracer's stack position is determined each time the control-Y is used to start tracing, so if the program is especially cute press after such instructions and type control-Y . If you don't disengage DOS for the tracer or the victim, and don't use a J command on the COUT routine, DOS will hang you forever the first time you try to trace a program that prints via COUT- basically, DOS will store the registers forever in spots not in the zero page (therefore not automatically fixed up). Use one of the above solutions to this problem! The same holds for KEYIN! Sadly, there is no good fix for this... DOS is simply not "re-entrant", ie it does not allow its routines to call itself in this way. The JSR area command SUBSTITUTES the original return address on the stack with its own, and then REPLACES the old address when it's done. This means that stack-saving routines work, as well as routines that JSR somewhere and then look at the stack to see what area they are in (this method is used in most peripheral cards). Watch out for routines in the JSR area whose first instruction is a PLA or those which have a TXS instruction anywhere! They're usually up to no good as far as the JSR area command is concerned- use the Invisible command instead. There are a number of "hooks" into the tracer that are there for easy expansion by a seasoned programmer- don't change the tracer itself if you want extra functions in it! NEVER NEVER NEVER NEVER use to exit the program. Sometimes the victim will do interesting things to the reset vector, and the tracer does all sorts of things while it's running. Instead, type , that will get you out of the tracer. It is recommended that you turn off interrupts whenever you use the tracer if you normally turn them on. The tracer does not force this, but strange things can happen if you aren't careful! .cj Special hooks and locations for Advanced programmers .lj The following is a list of some useful locations in the tracer. The locations are given as an offset from the beginning of the tracer program, their internal name, and what is there. These are intended to remain static in future version unless otherwise noted. +$0 START - This contains a JMP instruction to the routine which sets up the control-Y and saves the zero page at start+$100. +$3 TRACE - This contains a JMP to the routine which begins tracing. +$6 INSTDSP - This holds a JMP to the routine that disassembles and displays the operation about to be performed. Normally $F8D0, change this to have a symbolic trace (you will need to know how your assembler stores labels). See the monitor routine at $F8D0 to see what needs to be done. +$9 NEWCMD - Normally RTS, change this to a JMP to your own routine if you wish to add step commands besides ESC, P and T. The A register holds the key pressed. +$C NEWOP - Normally RTS, A will hold the opcode about to be executed, X will be 0. If A is changed, that instruction will be executed instead. Change this to execute "special" instructions, and pass a NOP in the A register. Passing a BRK (setting A to 0) will stop the trace. +$F MON - Normally a JMP to $FF65, this holds the address to jump to when a BRK is encountered or when ESC is pressed while in step mode. +$12 on - expansion area. +$20,$22,$24... LETA,LETB,LETC... - These addresses hold the address for a control-Y command in low-high order (2 bytes per letter). If it is stored in the area table (like B, I, and J) then it is simply the address. If it is executed immediately (like L, N, P, U, and C) then the address stored is one less than the starting address of the routine. +$54,$55,$56... TA,TB,TC... - These bytes indicate if the command is immediate or stored in the table (1 byte/letter). If it is immediately executed, a 0 is stored. If it is stored in a table, a 1 is stored. Other values (in particular $80) are reserved for future use. +$6E.. ONFLGTABLE - Holds the letters of the flags when the flags are on, normally NVXBDIZC with high bit set. +$76 OFFLGTABLE - Holds the letters of the flags when the flags are off, normally NVXBDIZC but is changed with the C command. +$80 CONTINUE - High bit of this byte is set if the trace is continuous (the T command from step mode). +$81 PCPOINT - Points to the next address to add an address in the PC table (used by the P command). +$82 TEMP - temporary register used by the tracer. +$83 TRACEP - Tracer's flags (stored when trace started). +$84 ACTNUM - Holds 5x the number of area actions. DO NOT SET this value to a non-multiple of 5! +$85 INVIS - If the high bit is set the second half of the line doesn't display, and if the sixth bit is set the first half is not supposed to be displayed (the I command sets the COUT vector to an RTS temporarily, which keeps any disassembly routine from displaying. If you create your own disassembly routine, check this flag before trying to figure out what characters to display and don't display if the sixth bit is set- that check will speed up invisible traces). +$8C SOFFSET - Holds how far the tracer's own stack is beyond the victim. Normally $60 (leaving room for 48 nested JSRs), the tracer's stack is determined by getting the victim's stack value and subtracting this value EACH time a trace is started. +$AA..AA TARGET - continuously changed to the instruction about to be executed (other than JMP and stack-using commands). +$9B MSKTABLE - Holds $80,$40..$01. Don't change them, but this table can come in handy. +$100 TZPAGE - Where the tracer stores the zero page. While the tracer is running the victim's zero page is stored here. Just before it executes an instruction of the victim it swaps its own zero page for this one, executes that instruction, and then swaps it back. This means that programs that hook onto the tracer shouldn't interfere with the victim zero page, just as the tracer itself doesn't. +$600.6FF PCTABLE - Holds the last 128 values of the program counter. Don't put anything here, and don't assume it will always be +$600. +$700.7FF AREATABLE - Holds the area instructions. Same warnings as PCTABLE. If you wish to add instructions to the tracer, use the routines mentioned earlier.