home *** CD-ROM | disk | FTP | other *** search
/ BCI NET 2 / BCI NET 2.iso / archives / programming / utilities / trapii_1_59.lha / TrapII.doc < prev    next >
Encoding:
Text File  |  1995-01-06  |  22.3 KB  |  485 lines

  1.  
  2.           DOC file for TrapIIv1.59 © 1990-1994 Nicolas Dade.
  3. Permission is hereby granted for non-commercial distribution and duplication.
  4.  last changed: Dec 30th, 1994
  5.  
  6. TrapII is of no use of you are not programming in a compiled language. It is
  7. of greatest use to an assembly language programmer.
  8.  
  9. Some of the things TrapII does fall in the category of hacks, so no
  10. guarantee is stated or implied by the author concerning the suitability of
  11. this program for any purpose what so ever, and the author can take no
  12. responsibility as to any damages, disappointments, etc... that follow the
  13. (attempted) use of this program. However, on the bright side, this program
  14. has worked as described on the author's 68010 A1000 and a 68030 A2000
  15. under WB2.1, and under WB3.0 with a 68040.
  16.  
  17. TrapII tries to help you (the programmer) by giving you much more
  18. information when your program (hereafter also referred to as the target)
  19. causes the 680x0 to enter exception handling code. That is, now when the
  20. target tries to read/write a word/long from/to an odd address, instead of
  21. getting a requestor, followed by a GURU which tells you it was you program
  22. that had the odd address, you get a (SMART refresh) window from TrapII...
  23.  
  24.   ­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­
  25.  
  26. The contents of the window TrapII opens for each trap are best explained
  27. by looking at an example:
  28. To see this example in real life, open a CLI, cd to the directory TrapII
  29. is in, and type the following two lines:
  30.  run <nil: >nil: TrapII
  31.  run demo
  32. (you MUST run the demo, and not simply execute the demo!)
  33. (you can also do this from workbench--doubleclick on TrapII, and then on Demo)
  34.  
  35. the source for demo is as follows:
  36.     TRAP    #$0 ; cause a TRAP #$0 trap
  37.     TRAP    #$F ; cause a TRAP #$F trap
  38.     MOVEQ.L #9,d0
  39.     MOVEQ.L #0,d1
  40. label ; this loop is used to demo [Trace] and [Next]
  41.     ADDQ.L  #1,d1
  42.     DBF.W   d0,label
  43.     TRAP    #1 ; cause a TRAP #$1 trap
  44.     BSR.S   subroutine ; used to demo [Next]
  45.     MOVE.W  D0,label+1 ; cause an address error trap
  46.     CLR.L   D0 ; actually, this instruction is never reached
  47.     RTS
  48.  
  49. subroutine
  50.     MOVE.W  #$FFFF,D2
  51.     MOVE.W  #$004F,D3
  52. sub_loop
  53.     DBF.W   D2,sub_loop
  54.     DBF.W   D3,sub_loop
  55.     RTS
  56.  
  57. You should see, in the workbench screen, a window looking like:
  58.  
  59.           +­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­+
  60.        line 1 |D nnnnnnnn  A nnnnnnnn FUNC -,-,----|
  61.         2 |1 nnnnnnnn  1 nnnnnnnn       ----    |
  62.         3 |2 nnnnnnnn  2 nnnnnnnn ADDR --------|
  63.         4 |3 nnnnnnnn  3 nnnnnnnn  /--------,--|
  64.         5 |4 nnnnnnnn  4 nnnnnnnn INST ----    |
  65.         6 |5 nnnnnnnn  5 nnnnnnnn (SP) nnnnnnnn|
  66.         7 |6 nnnnnnnn  6 nnnnnnnn  4() nnnnnnnn|
  67.         8 |7 nnnnnnnn SP nnnnnnnn  8() nnnnnnnn|
  68.         9 | SR nnnnnnnnnnnnnnnn    C() nnnnnnnn|
  69.        10 |    T-S--III---XNZVC   TRAP #$0       |
  70.        11 | PC nnnnnnnn/00000002,00  +­­­­­­­­­+
  71.        12 |PROC demo         |RemTask()|
  72.        13 |  nnnnnnnn   +­­­­­­­+­­­­+­+­­­­­­­+
  73.        14 |hh:mm:ss?m   |Restart| Next | Trace |
  74.           +­­­­­­­­­­­­­+­­­­­­­+­­­­­­+­­­­­­­+
  75.  
  76. (n,h,m and s represent values that may be different each time demo is run)
  77. (n represents a hex number, hh,mm,ss represent decimal)
  78. the information is grouped in fields, as shown below:
  79.  
  80.           +­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­+
  81.        line 1 |       |         |           |
  82.         2 | Data Regs | Addr Regs| Address Data|
  83.         3 |       |         |           |
  84.         4 |       |         |       AD       |
  85.         5 |    DR      |    AR    |_____________|
  86.         6 |       |         |           |
  87.         7 |       |         | Top of Stack|
  88.         8 |___________|__________|       SK       |
  89.         9 |              |_____________|
  90.        10 | SR & PC          |___Trap__TP__|
  91.        11 |________________________|       |
  92.        12 | Task TK   _____________| Gadgets   |
  93.        13 |__________|           GG       |
  94.        14 | Time TM  |           (or status) |
  95.           +­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­+
  96.  
  97.  ** DR **
  98. The data registers as they were then the trap occurred. The contents are
  99. given as hexadecimal unsigned longs.
  100. (When I write trap, I mean when the 68000 entered exception processing
  101. code. When I write TRAP, I mean the TRAP #n instruction.)
  102.  
  103.  ** AR **
  104. The address registers as they were then the trap occurred. The contents are
  105. given as unsigned longs. SP (A7), is the user stack pointer (USP), regardless
  106. of what the 68000's mode was when the trap occured.
  107.  
  108.  ** SR **
  109. The status register (SR) the 680x0 pushed on the stack when the trap occur-
  110. red, in binary. Below (line 10) is the meaning of the bits of the SR for a
  111. 68000.
  112.  
  113.  ** PC **
  114. The PC the 680x0 pushed on the stack when the trap occurred, given two
  115. ways.
  116. The first is the absolute value of the PC. This points to the instruction
  117. following the instruction that caused the trap, except in the case of bus,
  118. address and illegal instruction errors, in which case it may point to
  119. somewhere in the instruction that caused the trap. The PC points to the
  120. next instruction in memory, not the next it would have executed. Thus if
  121. the offending instruction was a JMP (to an odd address), for example, the
  122. PC points to just beyond the JMP in memory, not the address to jump to.
  123. This representation of the PC is useful if you are using a disassembler
  124. or debugger to look in memory. If you are using a disassembler,
  125. get is started and look at the address PC. You should see the code for
  126. demo, with the PC pointing to the second TRAP instruction (the TRAP #$f).
  127. The second representation of the PC (after the "/") is the offset from the
  128. start of the hunk the PC points in, followed by the hex number of that
  129. hunk (0 = first hunk). So 00000002,00 indicates that the PC points to an
  130. address 2 bytes beyond the start of the first hunk. A look at the source
  131. for demo (above) will show the PC points to the instruction following the
  132. first TRAP #0. This value is usefull for looking at the listing the assembler
  133. created, and for some disassemblers  and debuggers which look at the segment
  134. lists of loaded commands.
  135. (The hunk number must be less than 255, or no hunk number will be displayed)
  136.  
  137.  ** SK **
  138. These are the top four long words on the user stack. These are correct
  139. even if the USP is odd. Line 6 shows the topmost data. These are only useful
  140. in conjunction with a disassembler (if the trap occurred in a subroutine,
  141. you can see from where that routine was called).
  142.  
  143.  ** TP **
  144. This is a text description of the trap that occurred. Currently it says a
  145. TRAP #$0 instruction was reached. It can also say:
  146. Bus Error, Address Error, Illegal Instruction, Zero Divide, CHK Instruction,
  147. TRAPV Instruction, Privilege Violation, Trace, Line 1010 Emulation, Line
  148. 1111 Emulation, Next (used with [Next]), and, if the trap type is none of
  149. the above, trap nnnnnnnn, where nnnnnnnn is the number of the trap that
  150. occurred.
  151.  
  152.  ** TK **
  153. This field tries to identify what task/process caused the trap. First
  154. (line 12) are the letters "PROC" or "TASK", stating whether the target is
  155. a task or a process. In this case, demo was started by a CLI, and so is a
  156. process.
  157. To the right of this are up to 19 characters of text. These are the "best"
  158. name for the target TrapII could find:
  159. If the target is a task, then this is the name of the task, as pointed to
  160. by LN_NAME(target task structure).
  161. If the target is a process, then
  162.  If the target has a CLI, then
  163.   If the CLI has a non null command name (pointed to by cli_CommandName),
  164.    then this is that command name.
  165. else this is the task name, just as if the target were a task.
  166. Below (line 13) gives the address of the target's task/process structure.
  167.  
  168.  ** TM **
  169. This is the system time at which the trap occurred. This is not very exact,
  170. but it will allow you to look at multiple TrapII windows, and determine
  171. which is the "freshest." The time is the ONLY numerical value that is not
  172. in hexadecimal in the TrapII windows.
  173.  
  174.  ** GG **
  175.  
  176.  [Restart] (or the R key)
  177. This sets the target running where it stopped with the trace bit cleared.
  178. This only works if a set of conditions are all met:
  179.  [RemTask()] has not been used.
  180.  The trap was due to a TRAP #n instruction or a Trace (or a Next, which is
  181.   really a TRAP #$n).
  182.  The target has some signal bits allocated that are not used by its exception
  183.   code. (as of now, signal bits 0-15 are guarenteed to fall in this category,
  184.   so don't worry about this)
  185.  And the target has entered a wait state. (the target has proceeded out of
  186.   my trap handler, to some shutdown code. Again, this isn't important unless
  187.   some other higher priority task has blocked the target from running)
  188. This function allows TrapII to be used to halt/look at your program at
  189. selected places by putting a TRAP instruction where you want to halt and
  190. look at your program, or restart your program to full speed execution after
  191. you have stopped it.
  192. Once the target has been restarted, the gadgets are replaced by the text
  193. "Target Restarted" until the target enters its exception code again (say,
  194. from a TRAP #$n which you placed in your code, or [Next]'s ILLEGAL at the
  195. exit of the subroutine which you [Next]ed into).
  196.  
  197.  [Trace] (or the T key)
  198. This sets the target running where it stopped with the trace bit set. Same
  199. conditions as for [Restart] apply.
  200. It's implementation looks at the next instruction. If it is a TRAP, TRAPV,
  201. and V is set, or BKPT, then the instruction is just executed, with the
  202. trace bit off.
  203. If the instruction is a CHK or a DIV, no tracing is tried, because the
  204. potential double trap due to CHK being out of bounds or a DIV by 0 and the
  205. Trace sends the TrapII trap handler into a guru! Therefore a "Next" is always
  206. used when you try to [Trace] these instructions (this is done automatically
  207. for you, you just press [Trace])
  208. Note that this does not handle all cases: especially odd address errors.
  209. If you trace over an odd address error, you will GURU because TrapII cannot
  210. handle multiple exceptions at once (it's not TrapII's problem, it's the
  211. exec.library's)
  212.  
  213.  [Next] (or the N key)
  214. This only works on a few instructions. It puts a ILLEGAL just beyond the
  215. next instruction, then sets the target running where it stopped with the
  216. trace bit cleared. It only works on BCC, BSR, DBCC and JSR instructions,
  217. and uses the address of the instruction following the BCC, BSR, DBCC or
  218. JSR, and on the BRA.s, BRA and JMP instruction, where it uses the address
  219. on the stack (this is an easy speed up when a subroutine ends with the
  220. calling of another subroutine) (if you try to [Trace] a CHK or DIV, the
  221. [Next] function is used, but you cannot explicitly [Next] a CHK or DIV).
  222. The idea of this is to allow subroutines (or system calls) that you know
  223. work/don't want to trace through to execute a full speed, but still stop
  224. the target when it returns back to the subroutine level at which you were.
  225. Once the target stops because of the ILLEGAL, the word I changed in order
  226. to put a ILLEGAL is restored, and the task is stopped for a Next trap.
  227. [Next], of course, does not work if you are in ROM, since it must alter the
  228. next instruction.
  229. While a [Next] is being happening, the window is not closed, but the
  230. gadgets are replaced by "-Doing [Next]-" to tell you that the target is
  231. executing.
  232. NOTE: If you close the window of a task which is [Nexting] then the code
  233. is restored; you have essentially done a [Restart].
  234. You may nest [Next]'s as much as you like. (until you run out of memory,
  235. actually)
  236. NOTE: if the subroutines called are such that the target never gets back
  237. to the instruction following the BCC, BSR, DBCC or JSR, or the address on
  238. the stack at a BRA or JMP then by doing a [Next] you have essentially done a
  239. [Restart], since the target will never get back to the ILLEGAL which would
  240. stop it. You will not confuse TrapII by doing this, but remember that the
  241. instruction (or first word thereof) which was changed to a ILLEGAL is not
  242. restored until THAT ILLEGAL is reached.
  243. NOTE: since [Next] changes your code, if several tasks are running on the
  244. same code, all of them will react to the change; the target will find a
  245. Next exception, the others will find an ILLEGAL instruction, and will not
  246. not be able to restart.
  247. NOTE: since [Next] reacts differently for a BRA and a BCC, you cannot [Next]
  248. through a subroutine which you get to by a BCC.
  249.  
  250.  [RemTask()]
  251. This does a RemTask(target). It is provided in order to unclutter your
  252. system task list of "old" invocations of your program OR to stop any excep-
  253. tion code from executing. If the target uses exception code, then you have
  254. better use this if you don't want your exception code to execute; TrapII
  255. does nothing to halt exception processing.
  256. After this function is used, [Restart] and such can no longer be used.
  257. This function does not close the TrapII window.
  258. This function does not free any memory used by the target, not even the
  259. target's TC_MEMENTRY. The TC_MEMENTRY list header is restored after the
  260. RemTask() for your own post-death enjoyment.
  261.  
  262. In addition:
  263.  
  264.  The Close Window Gadget
  265. This closes the TrapII window, and leaves the target as it is. If the
  266. target is not executing ([Restart], [Trace] or [Next]), and [RemTask()] has
  267. not been used, then the target is in a wait state, waiting for nothing
  268. (Wait(0)).
  269. If, during a [Restart], [Trace] or [Next] you close the window, then if some
  270. of you code has been changed in order to do a [Next], that code is restored
  271. in the process of closing the window. This means that if you close the
  272. window during a [Next] then you could just as well have done a [Restart],
  273. since the instruction(s) which would have stopped the target at the exit of
  274. the subroutine(s) is (are) now gone.
  275.  
  276.  The Zoom Gadget
  277. This shrinks the window to a small bar. To reopen the window, click the
  278. the zoom gadget again.
  279.  
  280. Back to the demo:
  281. Since our trap is a TRAP #0 instruction, we can use [Restart] to continue.
  282. Press it now. The task "demo" is restarted, executes the next instruction,
  283. TRAP #$F, enters exception handling code, which culminates in TrapII
  284. updating its window for the target with this new information.
  285. This is another TRAP #n instruction trap, so nothing much is new (except
  286. the PC has incremented by 2).
  287. Now lets try to trace through the loop. Press [Trace]. The task "demo"
  288. is restarted, but because the trace bit is set, one instruction executes
  289. and then the CPU internally generates trace exception, which causes it
  290. to enter exception handling code, and eventually TrapII undates the
  291. window to inform you of this. Keep pressing [Trace] and see the PC run
  292. through the loop, d0 count down and d1 count up.
  293. When you are tired of this, press [Restart]. Now the task "demo" is
  294. restarted with the trace bit cleared, so now it runs full speed until it
  295. finds the TRAP #$1 at the end of the loop. Since the next instruction
  296. is a BSR.S, we can use [Next] to let the subroutine execute at full speed
  297. but yet gain control of the target back once the subroutine is finished.
  298. Press [Next], and the subroutine counts down, returns, and then causes
  299. a Next trap. [Next] is a way to "trace" through subroutines that you are
  300. not interrested in actually [Trace]ing through (like system calls or
  301. subroutines that you know (ha!) have no bugs). And we could have used
  302. [Next] to trace through our dbf.w loop by pressing [Next] when the PC
  303. pointed to the dbf.w instruction. That is usefull when there is not
  304. TRAP instruction following the loop.
  305. Press [Restart] to send the target going again until it causes an address
  306. error as it tries to load from the odd address label+1.
  307. Now the AD field is filled in.
  308.  
  309.  ** AD **
  310. This shows the information the 680x0 dumps in its stack frame when a bus or
  311. address error occurs. It should look like:
  312.  
  313.             ­­­­­­­­­­­­­­+
  314.         line 1     FUNC W,I,UsDt|
  315.              2          33c1    |
  316.              3     ADDR nnnnnnnn|
  317.              4      /00000009,00|
  318.              5     INST 33c0    |
  319.  
  320. FUNC shows the specific information concerning the memory access that
  321. caused the bus/address error.
  322. The first letter gives the direction of the access, W = write, R = read.
  323. The second letter gives what state the 68000 was in when the error occured
  324. I = executing an instruction, N = not executing an instruction (responding
  325. to an interrupt, another trap, etc...)
  326. The third field represents the function code that was on pins FC0,FC1 and
  327. FC2 of the 68000. It can be:
  328. UsDt...user data ; accessing source or destination
  329. UsPg...user program ; reading an instruction
  330. SpDt...supervisor data
  331. SpPg...supervisor program
  332. IAck...interrupt acknowledge
  333. ????...something else TrapII doesn't know how to interpret.
  334. Below (line 2) is the whole hex value of the function code the 68000 dumped
  335. on the stack. This is only useful if TrapII's interpretation of this value
  336. is not enough.
  337.  
  338. ADDR shows the address that caused the bus/address error. It is given
  339. first as an absolute address (line 3), then as an offset and hunk number
  340. (line 4), like the PC.
  341.  
  342. INST shows the hex value of the instruction the 68000 was executing when the
  343. trap occurred.
  344.  
  345.   ­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­
  346.  
  347. How TrapII works:
  348. (limited overview, skips many details, but lets you know what happens to
  349. the target)
  350.  
  351. TrapII adds to the AddTask() call a bit of code that compares the new
  352. task's trap code (TC_TRAPCODE) with both the system default (TaskTrapCode*)
  353. and the TC_TRAPCODE of TrapII itself. This is because TC_TRAPCODE of a
  354. task started by a CLI or workbench is not the system default (I don't know
  355. why). If the new task is using either, then TC_TRAPCODE is changed to
  356. point to TrapII's trap handler.**
  357.  
  358. Then TrapII waits for a signal from its trap handler.
  359.  
  360. When a new task causes a trap to occur (and has not installed its own trap
  361. handler), TrapII's handler is executed.
  362. The handler dumps the 680x0's registers, the 680x0's stack frame in a
  363. buffer, signals TrapII, and invents a new stack frame that clears T and S
  364. bits of the SR, and changes the PC to point to TrapII's target_wait code,
  365. points the USP to TrapII's ministack, and rte's.
  366.  
  367. The target now executes the target_wait code, which does a Wait(0).
  368.  
  369. TrapII is reawakened, opens or redraws a window and all that, and goes back
  370. to sleep.
  371.  
  372. When [Restart], [Trace] or [Next] is hit, then the target's wait stack frame
  373. is altered to point to TrapII's restart_target code, and the target is
  374. reawakened by changing TC_SIGWAIT to be all possible non-exception causing
  375. signals, and then Signal()ing the target.
  376.  
  377. The target now executes the restart_target code, which restores TC_SIGRCEVD,
  378. the registers to what was displayed in the TrapII window, and control
  379. proceeds to where the target left off.
  380.  
  381. [Next] is done by computing the length of the BSR[.S] or JSR, or getting
  382. the "return address" off the top of the stack, and putting a BKPT #0 at that
  383. next instruction. Then, whenever that task traps, if it trapped because
  384. of that BKPT #0, then it is cosidered a Next trap, the word altered to put
  385. the BKPT #0 is restored.
  386. You can see this for yourself: [Next] into a loop that contains a TRAP #n.
  387. Once the target has hit the TRAP #$n, look at the instruction following the
  388. BSR or JSR you used to enter the subroutine. It will be the BKPT #0 TrapII
  389. put there. Once you [Restart] the target, and it exits the subroutine,
  390. TrapII's BKPT #0 is reached, and the code is restored to what is was
  391. before, which your disassembler will show you if you look at that
  392. address again.
  393.  
  394. * TrapII does NOT change TaskTrapCode(execbase), because I do not see any
  395. advantage in doing this; I found no task that used it.
  396.  
  397. ** This way, if a task is going to use its own trap handler, either by
  398. later changing the value TC_TRAPCODE, or by having the task's launcher set
  399. it, TrapII will not get in the way.
  400. This also means any task started before TrapII was run will still be using
  401. the system's trap handler, which is why `run' was required to run the demo.
  402.  
  403.   ­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­
  404.  
  405. Limitations, Problems & Additional Notes:
  406.  
  407. Because of the way the trap handler and TrapII interact, there is a short
  408. time period between the time the trap occurred and the window is opened
  409. during which a second trap, will not be reported by TrapII. However, the
  410. offending task will be put into a perpetual wait state (Wait(0)).
  411. This time period lasts from when the trap occurs to a bit before the window
  412. is opened. This includes two AllocMem() calls.
  413.  
  414. It is not such a good idea to have TrapII running at a very low priority;
  415. In order to display its windows, TrapII does have to run. Priority 0 is
  416. good enough in most cases, but if you have customized the priorities of
  417. various tasks on your system, then you may need to run TrapII at a different
  418. priority.
  419.  
  420. TrapII will only effect tasks started (AddTask()) after it was run.
  421. This means that if you open a CLI, type "run trapii", then "your program",
  422. your program will NOT be using TrapII's trap handler, because "your program"
  423. is running using the CLI's task structure, and that was added to the system
  424. when the CLI was opened--before TrapII was run. However, if you typed "run
  425. your program", "your program" would use TrapII's trap handler, because run
  426. started a new task for "your program", and that task was started after
  427. TrapII was run.
  428.  
  429.   ­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­
  430.  
  431. Use:
  432.  
  433. Because targets started different ways seem to have different trap handling
  434. code, in order to be sure TrapII will work, TrapII must be started in the
  435. same manner the target will be started. For this reason, TrapII can be
  436. started from both a CLI or Workbench. It is true that sometimes, on some
  437. setups, with some programs running or not running, that a TrapII started
  438. one way can trap programs started another. However this behavior cannot be
  439. guaranteed; if it works on your system, use it if you like, but remember
  440. if, all of a sudden, TrapII isn't intercepting your program's errors any
  441. more, you will have to start TrapII the way I recommend.
  442.  
  443. NOTE: the author has used this program to debug AmigaDOS device handlers
  444. under 1.3 and 2.04/2.1 by starting TrapII running with
  445.  run TrapII
  446. from a CLI. I guess the DOS's startup-a-handler routine uses the same trap
  447. handler as run. However C= makes no promises that this will work forever.
  448.  
  449. WB:
  450. just double-click on icon
  451.  
  452. CLI:
  453. Here it seems that whether a task is started by:
  454.  target_name
  455. or with a run, it doesn't change the trap handler so you can start TrapII
  456. with:
  457.  run TrapII
  458. or, better, with:
  459.  run <nil: >nil: TrapII
  460.  
  461. If TrapII must be started at a non zero priority (let's use 3), then you
  462. can use either:
  463.  ChangeTaskPri 1
  464.  Run TrapII
  465. or better,
  466.  run <nil: >nil: ChangeTaskPri 1 +
  467.  TrapII
  468.  
  469. Now clever users may well be thinking of putting these last two
  470. lines in their startup-sequence, and that works for the author under
  471. 2.x.
  472.  
  473.   ­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­
  474.  
  475. I can be reached through:
  476.  
  477.  Nicolas Dade
  478.  405 W Delaware
  479.  Urbana, IL, 61801    (USA)
  480.  
  481. or nicolas-dade@uiuc.edu
  482.  
  483.   ­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­
  484.  
  485.