Path: utzoo!utgpu!jarvis.csri.toronto.edu!clyde.concordia.ca!uunet!mcsun!unido!sbsvax!roeder From: roeder@sbsvax.UUCP (Edgar Roeder) Newsgroups: comp.sys.atari.st Subject: Re: Changing Rez: What we _really_ want to know Summary: All about terminate handlers Message-ID: <2009@sbsvax.UUCP> Date: 4 Jan 90 14:50:38 GMT References: <8912301917.AA19200@TIS.COM> Organization: Universitaet des Saarlandes, Saarbruecken, W-Germany Lines: 320 In article <8912301917.AA19200@TIS.COM>, dmb@TIS.COM (David M. Baggett) writes: > P.S.> Could some kind soul please repost the "all about terminate handlers" > article here? Thanks. Sorry, i don't have this document, but i have prepared another one. The file is in Info-format, suitable for reading with GNU-emacs, but it should be readable as text too. Hope this helps! - Edgar ---------------------- cut here ------------------------ Info file: etv_term, -*-Text-*- produced by texinfo-format-buffer from file: etv_term.txi This file documents the TOS terminate handler etv_term and what you can do with it. File: etv_term Node: Intro, Prev: Concept Index, Up: Top, Next: Top The Terminate handler ********************* Some people on the net asked for documentation about the terminate vector. Here is what i have collected over the past few years. Some of the details required a lot of experimenting, but all the presented solutions work with all TOS versions (although undocumented features are used). - Edgar File: etv_term Node: Top, Prev: Intro, Up: (dir), Next: Actions When is the Terminate Handler called ? ====================================== The logical vector #0x102 "etv_term" at address 0x400 is called by `Pterm()' before a program is terminated. This can have several causes. Either 1. an explicit call to * the gemdos functions `Pterm0()', `Pterm()' or `Ptermres()' or * `trap #2' with argument 0 in register D0 or 2. another program (a resident monitor like templmon or a shell or an accessory) directly calls one of the above functions or 3. `Pterm()' (in early TOS versions `Pterm0()') is called from one of the exception routines (after some bombs or mushrooms have been drawn) or 4. `Pterm(-32)' is called when ^C is pressed during screen-I/O Since in cases 2--4 above the user program normally has not initiated the program termination, the terminate vector is the last (and only) chance to do some cleanup work before execution is finished. * Menu: * Actions:: What can a terminate handler do * Call:: How the handler is called * Installation:: How to install a handler * Concept Index:: File: etv_term Node: Actions, Prev: Top, Up: Top, Next: Call What you want to do in the Handler ================================== Before Program Termination (cases 2 + 3) ---------------------------------------- * give some good-bye message to the user * flush buffered output streams (files are closed after the call to etv_term by TOS) * deinstall modified system vectors (*Note Installation::) * modify the return code to signal an error condition to the calling program (*Note Return Code::) * prevent termination (probably after asking the user) and go to a safe place to restart the program (for example the prompt in an interpreter) or enter a debugger (in case 3 above) If ^C is pressed (case 4) ------------------------- * ignore it completely (*Note Ignore ^C::) * ask the user wether to ignore it or to terminate the program * go to a safe place and cancel the current action of the program So what we need to know about terminate handlers are the restrictions, which TOS-functions can be called, how we can access the parameter to `Pterm' and how we can resume execution at the place where ^C was pressed. * Menu: * Restrictions:: What can be called from a handler * Return Code:: access the program's return code * Ignore ^C:: avoid program termination File: etv_term Node: Call, Prev: Actions, Up: Top, Next: Installation How is the handler called ? =========================== The etv_term vector is called via `jsr' in supervisor mode. The calling routine (`Pterm()') was called in C with one parameter (the return code for the program) on the stack (*Note Return Code::). File: etv_term Node: Installation, Prev: Call, Up: Top, Next: Concept Index How can i install my own routine ? ================================== If you are in supervisor mode (after a call to `Super()' from user mode with a parameter != 1L or in a routine executed by `Supexec()') you can directly set the variable at `0x400' to the address of your routine: *((void (**)()) 0x400) = own_routine; More portable and the recommended way is a call to the Bios function #5 (`Setexc()'): old_routine = (void (*)()) Setexc(0x102, own_routine); The terminate handler itself has no parameter and no return value: void own_routine(void); File: etv_term Node: Restrictions, Prev: Actions, Up: Actions, Next: Return Code What can be done in a terminate handler ? ========================================= This depends on what should happen after the handler has finished. We have three cases: 1. cleanup and terminate execution of the program 2. resume execution at some safe point (eg. top level of program) 3. ignore the termination request (*Note Ignore ^C::) In case 1 we should restore the original terminate handler before we leave our program. This can be done like the installation with Setexc(0x102, old_routine); If our terminate handler is not removed before other programs are started, the handler will also be called when these other programs are about to be terminated. In a shell this can be used to control the behavior of other programs with respect to ^C. In case 2 we should return to user mode since this is the normal mode of execution for user programs. This can be done with a call to the gemdos function `Super()' after calling `longjmp()'. A sample code fragment could be this: #include jmp_buf global_exit; long stackpointer; void terminate_handler(void) { ... longjmp(global_exit,1); } toplevel_routine() { int ret; ... stackpointer = Super(0L); ret = setjmp(global_exit); Super(stackpointer); switch(ret) { ... } ... } In the cases 1 and 2 you may call any routines (including gemdos, bios, xbios, aes and vdi) since we leave the normal flow of execution anyway and so no information needs to be preserved. But you have to be careful, if `Pterm()' was called as the result of a processor exception (eg. bus error): probably some internal data structures are in an unstable state. In case 3 it is important to know where the `Pterm()'-call happened. As a thumb rule you can call xbios or bios safely from any place. But if you have switched to user mode (and want to decide between cases 1, 2 or 3 later), you cannot use the old userstack. Some people might prefer using the user stack in supervisor mode too. Also gemdos - if called from user mode - uses a part of the user stack below the user stack pointer (~40 bytes) to save some registers. And this is just the case if `Pterm()' was called via `trap #1'. File: etv_term Node: Return Code, Prev: Restrictions, Up: Actions, Next: Ignore ^C How can the argument to `Pterm()' be accessed ? =============================================== `Pterm()' is called from C. The argument to `Pterm()' can thus be found on the stack. Since it is also programmed in C we can access the parameter with the help of processor register A6 the frame pointer in C. `Pterm()' calls (*etv_term)() directly. At the entry of our routine A6 still points to the right frame. So we can get the return value for the program (the parameter of `Pterm()') simply as the word 8 bytes above the frame pointer (as usual in C). The value of the return code can be read and compared against -32 to decide wether ^C was pressed (or `Pterm(-32)' called). So we can distinguish the several causes for a `Pterm()'-call. We can also change this value on the stack to change our program's return value. If you are using KAOS-TOS (some patches to Blitter-TOS from the german magazine c't - don't ask me about it since TOS 1.4 is certainly better) the return value if ^C was pressed is different. Here it is -68 if ^C was pressed and -69 if an exception occured. File: etv_term Node: Ignore ^C, Prev: Return Code, Up: Actions How can ^C be ignored ? ======================= Several solutions exist for this problem. One is to use only functions ignoring ^C for screen-I/O (`Crawio()', `Crawcin()' or bios functions). But you can also use the terminate-vector for this task. TOS was programmed in C and the compiler did not know that `Pterm()' is not supposed to return to the calling routine. Now if `Pterm()' returns no surprising effects are observed and the execution goes on as if ^C was not pressed. But remember that this is not guaranteed to work to all eternity (but it works from TOS 1.0 - 1.4). To ignore ^C all you have to do in the terminate handler is to check for the return value -32 and before the rts instruction do an extra `unlk a6' to pop the frame of the C-function `Pterm()'. The program will continue as if ^C was never touched. File: etv_term Node: Concept Index, Prev: Installation, Up: Top, Next: Intro Concept Index ************* * Menu: * access return code: Return Code. * actions: Actions. * call: Call. * calling conditions: Top. * etv_term: Top. * get address of old handler: Installation. * ignore ^C: Ignore ^C. * install a new handler: Installation. * installation: Installation. * restrictions: Restrictions. * resume execution: Ignore ^C. * return code: Return Code. * stackpointer: Restrictions. * switch to user mode: Restrictions. * terminate handler: Intro. * terminate vector: Top. * test return code: Return Code. Tag table: Node: Intro184 Node: Top619 Node: Actions1810 Node: Call3154 Node: Installation3484 Node: Restrictions4139 Node: Return Code6592 Node: Ignore ^C7771 Node: Concept Index8674 End tag table -- Mail: Edgar R\"oder E-Mail: roeder@cs.uni-sb.de Liesbet-Dill-Stra\ss e 3 D-6602 Dudweiler -o- -o- W-Germany ^ Phone: 06897/74643 '---'