DOC file for TrapIIv1.11b й 1992 Nicolas Dade. Permission is hereby granted for non-commercial distribution and duplication. last changed: Feb 4th, 1992 TrapII is of no use of you are not programming in a compiled language. It is of greatest use to an assembly language programmer. Some of the things TrapII does fall in the category of hacks, so no guarantee is stated or implied by the author concerning the suitability of this program for any purpose what so ever, and the author can take no responsibility as to any damages, disappointments, etc... that follow the (attempted) use of this program. However, on the bright side, this program has worked as described on the author's A1000 under WB1.3. TrapII tries to help you (the programmer) by giving you much more information when your program (hereafter also referred to as the target) causes the 680x0 to enter exception handling code than does the normal system default. That is, now when the target tries to read/write a word/long from/to an odd address, instead of getting a requestor "system requestor/software error/task held/finish all disk activity/cancel to debug/reset", followed by a GURU which tells you it was you program that had the odd address, you get a (SMART refresh) window from TrapII... ннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннн New in 1.3: (1.1 & 1.2 were just bug fixes) 1) Safer task restarting: version 1.2 could make a mess of the target's signals if there were interrupts and other tasks talking to the target. Because of this new code not only is Forbid() used (correctly this time), but for about 20 clock cycles (68000), Disable() is too. 2) target_wait code changed to support Disable(). New in 1.4: 1) Got rid of the screen moving thing, and changed the screen to front so that it calls ScreenToFront(wd's screen) instead WBenchToFront() New in 1.6: (1.5 was bug fix) 1) Changed window gadgets so that [Off] is gone (use rmb), [OK/Ignore] is gone (use close gadget), and a new one, [Trace], is given. With trace you can step through your code instruction by instruction, until you are tired of this, and just hit [Restart]. 2) Safe handling of USP. Now your task can use a7 for anything it desires- it does not have to point to a stack. TrapII allocates a mini-stack for the target to use when the target is being shutdown and restarted. 3) Got rid of the ScreenToFront() when the TrapII window opens altogether. New in 1.7 1) Added [Next] gadget & function. 2) Window is no longer closed after [Restart], [Trace] or [Next] is pressed. This saves a lot of time on a clogged wb screen, and reminds you that the task is off doing something. 3) Changed the offset/segment finding code so that if the target is started from WB the offset/segment can still be found, and so that if the segment list seems too long (i.e. corrupted), we are not in a perpetual loop New in 1.8 1) Added keyboard equivalents for the gadgets: [Next] = N [Trace] = T [Restart] = R 2) Fixed bug in TrapII which was generating address errors (which I traced by having two TrapIIs running one on top of the other, and using the second to trace through the first!) New in 1.9 1) Added keyboard equivalents for close and sleep Close/Ok/Ignore = O Sleep = Z 2) Added support for [Next]ing through loops which end in BCC 3) Window deiconifies when a key is pressed (well, most keys) New in 1.10 1) Fixed window closing code in hope of avoiding crashes. New in 1.11 1) Found that bug!, and fixed it for good. (the one where if you click twice on anything really quickly then you get an addr error). 2) Changed wd closing code to use arp.library's CloseWindowSafely() New in 1.11b 1) Bug fix of bug in version distributed to ab20 (sorry). ннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннн The contents of the window TrapII opens for each trap are best explained by looking at an example: To see this example in real life, open a CLI, cd to the directory TrapII is in, and type the following two lines: run nil: TrapII run demo (the source for demo is as follows: TRAP #$0 ; cause a TRAP #$0 trap TRAP #$F ; cause a TRAP #$F trap MOVEQ.L #9,d0 MOVEQ.L #0,d1 label ; this loop is used to demo [Trace] ADDQ.L #1,d1 DBF.W d0,label TRAP #$1 ; cause a TRAP #$1 trap after the loop BSR.S subroutine ; used to demo [Next] MOVE.W D0,label+1 ; cause an address error trap CLR.L D0 ; actually, this instruction is never reached RTS subroutine MOVE.W #$FFFF,D2 MOVE.W #$004F,D3 sub_loop DBF.W D2,sub_loop DBF.W D3,sub_loop RTS ) (If you like, the demo can also be run from workbench. Double-click TrapII, then double-click demo.) You should see, in the workbench screen, a window looking like: +нннннннннннннннннннннннннннннннннннн+ line 1 |D nnnnnnnn A nnnnnnnn FUNC -,-,----| 2 |1 nnnnnnnn 1 nnnnnnnn ---- | 3 |2 nnnnnnnn 2 nnnnnnnn ADDR --------| 4 |3 nnnnnnnn 3 nnnnnnnn /--------,--| 5 |4 nnnnnnnn 4 nnnnnnnn INST ---- | 6 |5 nnnnnnnn 5 nnnnnnnn (SP) nnnnnnnn| 7 |6 nnnnnnnn 6 nnnnnnnn 4() nnnnnnnn| 8 |7 nnnnnnnn SP nnnnnnnn 8() nnnnnnnn| 9 | SR nnnnnnnnnnnnnnnn C() nnnnnnnn| 10 | T-S--III---XNZVC TRAP #$0 | 11 | PC nnnnnnnn/00000002,00 +ннннннннн+ 12 |PROC demo |RemTask()| 13 | nnnnnnnn +ннннннн+нннн+н+ннннннн+ 14 |hh:mm:ss |Restart| Next | Trace | +ннннннннннннн+ннннннн+нннннн+ннннннн+ (n,h,m and s represent values that may be different each time demo is run) (n represents a hex number, hh,mm,ss represent decimal) the information is grouped in fields, as shown below: +нннннннннннннннннннннннннннннннннннн+ line 1 | | | | 2 | Data Regs | Addr Regs| Address Data| 3 | | | | 4 | | | AD | 5 | DR | AR |_____________| 6 | | | | 7 | | | Top of Stack| 8 |___________|__________| SK | 9 | |_____________| 10 | SR & PC |___Trap__TP__| 11 |________________________| | 12 | Task TK _____________| Gadgets | 13 |__________| GG | 14 | Time TM | (or status) | +нннннннннннннннннннннннннннннннннннн+ ** DR ** The data registers as they were then the trap occurred. The contents are given as hexadecimal unsigned longs. (When I write trap, I mean when the 68000 entered exception processing code. When I write TRAP, I mean the TRAP #n instruction.) ** AR ** The address registers as they were then the trap occurred. The contents are given as unsigned longs. SP (A7), is the user stack pointer (USP), regardless of what the 68000's mode was when the trap occured. ** SR ** The status register (SR) the 680x0 pushed on the stack when the trap occur- red, in binary. Below (line 10) is the meaning of the bits of the SR for a 68000. ** PC ** The PC the 680x0 pushed on the stack when the trap occurred, given two ways. The first is the absolute value of the PC. This points to the instruction following the instruction that caused the trap, except in the case of bus and address errors, in which case it may point to somewhere in the instruc- tion that caused the trap. The PC points to the next instruction in memory, not the next it would have executed. Thus if the offending instruction was a JMP (to an odd address), for example, the PC points to just beyond the JMP in memory, not the address to jump to. This representation of the PC is useful if you are using a disassembler/debugger to look in memory. If you are using a disassembler, get is started and look at the address PC. You should see the code for demo, with the PC pointing to the second TRAP instruction (the TRAP #$f). The second representation of the PC (after the "/") is the offset from the start of the hunk the PC points in, followed by the hex number of that hunk (0 = first hunk). So 00000002,00 indicates that the PC points to an address 2 bytes beyond the start of the first hunk. A look at the source for demo (above) will show the PC points to the instruction following the first TRAP #0. This value is usefull for looking at the listing the assembler created, and for some disassemblers and debuggers which look at the segment lists of loaded commands. (The hunk number must be less than 255, or no hunk number will be displayed) ** SK ** These are the top four long words on the user stack. These are correct even if the USP is odd. Line 6 shows the topmost data. These are only useful in conjunction with a disassembler (if the trap occurred in a subroutine, you can see from where that routine was called). ** TP ** This is a text description of the trap that occurred. Currently it says a TRAP #$0 instruction was reached. It can also say: Bus Error, Address Error, Illegal Instruction, Zero Divide, CHK Instruction, TRAPV Instruction, Privilege Violation, Trace, Line 1010 Emulation, Line 1111 Emulation, Next (used with [Next]), and, if the trap type is none of the above, trap nnnnnnnn, where nnnnnnnn is the number of the trap that occurred. ** TK ** This field tries to identify what task/process caused the trap. First (line 12) are the letters "PROC" or "TASK", stating whether the target is a task or a process. In this case, demo was started by a CLI, and so is a process. To the right of this are up to 19 characters of text. These are the "best" name for the target TrapII could find: If the target is a task, then this is the name of the task, as pointed to by LN_NAME(target task structure). If the target is a process, then If the target has a CLI, then If the CLI has a non null command name (pointed to by cli_CommandName), then this is that command name. else this is the task name, just as if the target were a task. Below (line 13) gives the address of the target's task/process structure. ** TM ** This is the system time at which the trap occurred. This is not very exact, but it will allow you to look at multiple TrapII windows, and determine which is the "freshest." The time is the ONLY numerical value that is not in hexadecimal in the TrapII windows. ** GG ** [Restart] (or the R key) This sets the target running where it stopped with the trace bit cleared. This only works if a set of conditions are all met: [RemTask()] has not been used. The trap was due to a TRAP #n instruction or a Trace (or a Next, which is really a TRAP #$n). The target has some signal bits allocated that are not used by its exception code. (as of now, signal bits 0-15 are guarenteed to fall in this category, so don't worry about this) And the target has entered a wait state. (the target has proceeded out of my trap handler, to some shutdown code.) This function allows TrapII to be used to halt/look at your program at selected places by putting a TRAP instruction where you want to halt and look at your program, or restart your program to full speed execution after you have stopped it. Once the target has been restarted, the gadgets are replaced by the text "Target Restarted" until the target enters its exception code again (say, from a TRAP #$n which you placed in your code, or [Next]'s TRAP #$0 at the exit of the subroutine which you [Next]ed into). [Trace] (or the T key) This sets the target running where it stopped with the trace bit set. Same conditions as for [Restart] apply. Actually first looks at the next instruction. If it is a TRAP, TRAPV, and if the execution of the instruction would generate an exception, then the instruction is just executed, with the trace bit off. If the instruction is a CHK or a DIV, no tracing is tried, because the potential double trap due to CHK being out of bounds or a DIV by 0 and the Trace sends the TrapII trap handler into a guru! Therefore a "Next" is always used when you try to [Trace] these instructions (this is done automatically for you, you just press [Trace]) [Next] (or the N key) This only works on a few instructions. It puts a TRAP #n just beyond the next instruction, then sets the target running where it stopped with the trace bit cleared. It only works on BCC, BSR, DBCC and JSR instructions, and uses the address of the instruction following the BCC, BSR, DBCC or JSR, and on the BRA.s, BRA and JMP instruction, where it uses the address on the stack (this is an easy speed up when a subroutine ends with the calling of another subroutine) (if you try to [Trace] a CHK or DIV, the [Next] function is used, but you cannot explicitly [Next] a CHK or DIV). The idea of this is to allow subroutines (or system calls) that you know work/don't want to trace through to execute a full speed, but still stop the target when it returns back to the subroutine level at which you were. Once the target stops because of the TRAP #n, the word I changed in order to put a TRAP #n is restored, and the task is stopped for a Next trap. [Next], of course, does not work if you are in ROM, since it must alter the next instruction. NOTE: If you close the window of a task which is [Nexting] then the code is restored; you have essentially done a [Restart]. WARNING: you may NOT nest [Next] calls. Do not do a [Next] into a subroutine which contains a TRAP, and then [Next] inside that subroutine to a second one, or the first [Next] will be canceled (its Trap #n removed). WARNING: if the subroutines called are such that the target never gets back to the instruction following the BCC, BSR, DBCC or JSR, or the address on the stack at a BRA or JMP then by doing a [Next] you have essentially done a [Restart], since the target will never get back to the TRAP #n which would stop it. You will not confuse TrapII by doing this, but remember that the instruction (or first word thereof) which was changed to a TRAP #n is not restored until THAT TRAP #n is reached. While a [Next] is being done, the window is not closed, but the gadgets are replaced by "-Doing [Next]-" to tell you that the target is executing. NOTE: since [Next] changes your code, if several tasks are running on the same code, all of them will react to the change; the target will find a Next exception, the others will find a TRAP #$0. NOTE: since [Next] reacts differently for a BRA and a BCC, you cannot [Next] through a subroutine which you get to by a BCC. (Maybe in the next version I will split the Next function into two: Next to next instruction and Next to addr on stack) [RemTask()] This does a RemTask(target). It is provided in order to unclutter your system task list of "old" invocations of your program OR to stop any excep- tion code from executing. If the target uses exception code, then you have better use this if you don't want your exception code to execute; TrapII does nothing to halt exception processing. After this function is used, [Restart] and [Trace] can no longer be used. This function does not close the TrapII window. This function does not free any memory used by the target. In addition: The Close Window Gadget (or the O key) This closes the TrapII window, and leaves the target as it is. If the target is not executing ([Restart], [Trace] or [Next]), and [RemTask()] has not been used, then the target is in a wait state, waiting for nothing (Wait(0)). If, during a [Restart], [Trace] or [Next] you close the window, then if some of you code has been changed in order to do a [Next], that code is restored in the process of closing the window. This means that if you close the window during a [Next] then you could just as well have done a [Restart], since the instruction which would have stopped the target at the exit of the subroutine is now gone. The Right Mouse Button (or the Z key) This shrinks the window to a small bar. To reopen the window, make the bar window active, then click the right mouse button. Back to the demo: Since our trap is a TRAP #0 instruction, we can use [Restart] to continue. Press it now. The task "demo" is restarted, executes the next instruction, TRAP #$F, enters exception handling code, which culminates in TrapII updating its window for the target with this new information. This is another TRAP #n instruction trap, so nothing much is new (except the PC has incremented by 2). Now lets try to trace through the loop. Press [Trace]. The task "demo" is restarted, but because the trace bit is set, one instruction executes and then the CPU internally generates trace exception, which causes it to enter exception handling code, and eventually TrapII undates the window to inform you of this. Keep pressing [Trace] and see the PC run through the loop, d0 count down and d1 count up. When you are tired of this, press [Restart]. Now the task "demo" is restarted with the trace bit cleared, so now it runs full speed until it finds the TRAP #$1 at the end of the loop. Since the next instruction is a BSR.S, we can use [Next] to let the subroutine execute at full speed but yet gain control of the target back once the subroutine is finished. Press [Next], and the subroutine counts down, returns, and then causes a Next trap. [Next] is a way to "trace" through subroutines that you are not interrested in actually [Trace]ing through (like system calls or subroutines that you know (ha!) have no bugs). Press [Restart] to send the target doing again until it causes an address error as it tries to load from the odd address label+1. Now the AD field is filled in. ** AD ** This shows the information the 68000 dumps in its stack frame when a bus or address error occurs. It should look like: нннннннннннннн+ line 1 FUNC W,I,UsDt| 2 33c1 | 3 ADDR nnnnnnnn| 4 /00000009,00| 5 INST 33c0 | FUNC shows the specific information concerning the memory access that caused the bus/address error. The first letter gives the direction of the access, W = write, R = read. The second letter gives what state the 68000 was in when the error occured I = executing an instruction, N = not executing an instruction (responding to an interrupt, another trap, etc...) The third field represents the function code that was on pins FC0,FC1 and FC2 of the 68000. It can be: UsDt...user data ; accessing source or destination UsPg...user program ; reading an instruction SpDt...supervisor data SpPg...supervisor program IAck...interrupt acknowledge ????...something else TrapII doesn't know how to interpret. Below (line 2) is the whole hex value of the function code the 68000 dumped on the stack. This is only useful if TrapII's interpretation of this value is not enough. ADDR shows the address that caused the bus/address error. It is given first as an absolute address (line 3), then as an offset and hunk number (line 4), like the PC. INST shows the hex value of the instruction the 68000 was executing when the trap occurred. ннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннн How TrapII works: (limited overview, skips many details, but lets you know what happens to the target) TrapII adds to the AddTask() call a bit of code that compares the new task's trap code (TC_TRAPCODE) with both the system default (TaskTrapCode*) and the TC_TRAPCODE of TrapII itself. This is because TC_TRAPCODE of a task started by a CLI or workbench is not the system default (I don't know why). If the new task is using either, then TC_TRAPCODE is changed to point to TrapII's trap handler.** Then TrapII waits for a signal from its trap handler. When a new task causes a trap to occur (and has not installed its own trap handler), TrapII's handler is executed. The handler dumps the 68000's registers, the 68000's stack frame in a buffer, signals TrapII, and invents a new stack frame that clears T and S bits of the SR, and changes the PC to point to TrapII's target_wait code, evens the USP to TrapII's ministack, and rte's. The target now executes the target_wait code, which consists of just Wait(0). TrapII is reawakened, opens a new window and all that, and goes back to sleep. When [Restart], [Trace] or [Next] is hit, then the target's wait stack frame is altered to point to TrapII's restart_target code, and the target is reawakened. The target now executes the restart_target code, which restores the regis- ters to what was displayed in the TrapII window, and control proceeds to where the target left off. [Next] is done by computing the length of the BSR[.S] or JSR, or getting the "return address" off the top of the stack, and putting a TRAP #0 at that next instruction. Then, whenever that task traps, if it trapped because of that TRAP #0, then it is cosidered a Next trap, the word altered to put the TRAP #0 is restored, and the PC is backed up 2 bytes so that the next instruction is that which was originally there. (If you have a disassembler you can see this: [Next] into a loop that contains a TRAP #$n. Once the target has hit the TRAP #$n, look at the instruction following the BSR or JSR you used to enter the subroutine. It will be the TRAP #0 TrapII put there. Once you [Restart] the target, and it exits the subroutine, TrapII's TRAP #0 is reached, and the code is restored to what is was before, which your disassembler will show you too if you look at that address again.) Of course, this is in fact a form of that great crasher of harvard architecture machines, self-modifying code, but at the top of this doc file I did say that TrapII did some hacks, and this is one of them (just keep this in mind if you are using a cache). * TrapII does NOT change TaskTrapCode(execbase), because I do not see any advantage in doing this; I found no task that used it. ** This way, if a task is going to use its own trap handler, either by later changing the value TC_TRAPCODE, or by having the task's launcher set it, TrapII will not get in the way. This also means any task started before TrapII was run will still be using the system's trap handler, which is why `run' was required to run the demo. ннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннн Limitations, Problems & Additional Notes: Because of the way the trap handler and TrapII interact, there is a short time period between the time the trap occurred and the window is opened during which a second trap, will not be reported by TrapII. However, the offending task will be put into a perpetual wait state (Wait(0)). This time period lasts from when the trap occurs to a bit before the window is opened. This includes two AllocMem() calls. It is not such a good idea to have TrapII running at a very low priority; In order to display its windows, TrapII does have to run. Priority 0 is good enough in most cases, but if you have customized the priorities of various tasks on your system, then you may need to run TrapII at a different priority. TrapII will only effect tasks started (AddTask()) after it was run. This means that if you open a CLI, type "run trapii", then "your program", your program will NOT be using TrapII's trap handler, because "your program" is running using the CLI's task structure, and that was added to the system when the CLI was opened--before TrapII was run. However, if you typed "run your program", "your program" would use TrapII's trap handler, because run started a new task for "your program", and that task was started after TrapII was run. ннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннн Use: Because targets started different ways seem to have different trap handling code, in order to be sure TrapII will work, TrapII must be started in the same manner the target will be started. For this reason, TrapII can be started from both a CLI or Workbench. It is true that sometimes, on some setups, with some programs running or not running, that a TrapII started one way can trap programs started another. However this behavior cannot be guaranteed; if it works on your system, use it if you like, but remember if, all of a sudden, TrapII isn't intercepting your program's errors any more, you will have to start TrapII the way I recommend. NOTE: the author has used this program to debug AmigaDOS device handlers under 1.3 and a beta 2.0 by starting it running with run TrapII from a CLI. I guess the DOS's startup-a-handler routine uses the same trap handler as run. However C= makes no promises that this will work forever. WB: just double-click on icon CLI: Here it seems that whether a task is started by: target_name or with a run, it doesn't change the trap handler so you can start TrapII with: run TrapII or, better, with: run nil: TrapII If you are using arun (arp's run), then TrapII can be started with arun: arun >nil: TrapII noio because, it seems, arun uses the same trap handler than run (ArpRelease1.3) If TrapII must be started at a non zero priority (let's use 3), then you can use either: ChangeTaskPri 3 TrapII or: ChangeTaskPri 3 Run nil: TrapII pri 3 noio Now clever users may well be thinking doing this in their startup-sequence. This may work on your machine, but it does not on the author's. It seems the startup-sequence's trap handling code is not the same as later CLI's! However it might work with your setup, so don't let this dissuade you from trying. However, it does seem that a task started from workbench has the same trap handler as one started from a CLI (it seems everyone is using CreateProc()). ннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннн I can be reached through: Nicolas Dade 405 W Delaware Urbana, IL, 61801 (USA) or maybe nsd20463@uxa.cso.uiuc.edu ннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннн