dmb@TIS.COM (David M. Baggett) (12/31/89)
A bit of summary for those just tuning in: I asked if you could change rez without the Bios. Atari's Derek Mui said "No." Then Neil Forsyth asked if you could restore the desktop resolution from within a function installed at GEM process termination vector 0x102. Allan said: Yes. I wrote an off-the-cuff article "all about terminate handlers" on GEnie, then asked John Townsend to post it here. I don't know if he has, but among other things, it says you can make BIOS and XBIOS calls but not GEMDOS calls. Then Ken said: Neil asks if there are magic negative-offset line A variables which can be stuffed with appropriate values to fool GEM if an application changes res. The answer is: No. [AES has no published variables so you can't tell it about res changes] Derek Mui is very wise in the ways of the AES, and when he says there is no way, legal or illegal... believe him! And now (me): We believe! We believe! OK, OK, we know you can't tell AES and GEM about the rez change. But suppose we _don't care_ about GEM and AES and we just want the VDI/line A to work correctly. My problem isn't with GEM or AES, but rather with the VT52 emulator. Here's the scenario: 1) Run program from (medium rez) GULAM. 2) In program, change rez to low. AES is screwed. I don't care. 3) Process termination occurs. I switch back to medium rez _without the Bios_. 4) GULAM thinks the screen is only 40 columns wide. Bummer. Although Allan says you can call the Bios from within a routine installed as process termination handler (at 0x102), I sure couldn't. Adding the single line Setscreen(-1L, -1L, 1); causes my program to crash horribly. The point is, I just want to be able to duplicate the functionality of Setscreen without calling the Bios. Either that, or I'd like to find out what the secret is to calling Bios routines at process termination. Ideas? Thanks, Dave Baggett (dmb@TIS.COM) P.S.> Could some kind soul please repost the "all about terminate handlers" article here? Thanks.
bro@eunomia.rice.edu (Douglas Monk) (01/04/90)
Thought I might drop in my 2 cents: I typically write routines dostart() and doexit(exitval) in programs which run in low res but for which I want to use 80 columns: dostart saves the current resolution and if it is low res, changes to high res and plays with the palette (and whatever other changes I need, like turning on the cursor, etc.). Thus dostart contains a call Setscreen(-1L, -1L, 1) as needed - and it doesn't crash. doexit restores the original palette and resolution, and then calls exit(exitval). Thus doexit contains a call Setscreen(-1L, -1L, 0) as needed - and it doesn't crash. I just call doexit then wherever I would be calling exit otherwise. If the system crashes, I might be screwed up, but the worst would be to have the parent wind up in the wrong resolution with funny colors, and that can be fixed. But my programs do not crash :-) of course. I use a program like this from my auto folder - but only use bios and xbios calls in it. Doug Monk Disclaimer: These views are mine, not necessarily my organization's.
apratt@atari.UUCP (Allan Pratt) (01/04/90)
dmb@TIS.COM (David M. Baggett) writes: >Although Allan says you can call the Bios from within a routine installed >as process termination handler (at 0x102), I sure couldn't. Adding the >single line > Setscreen(-1L, -1L, 1); >causes my program to crash horribly. Well, this ought to work. I don't know why it wouldn't. Are you sure it was just that line? I use it all the time. I have modified a ray tracer so it shows its progress; it switches rez all over the place, and switches back (successfully!) if terminated with ^C or something. ============================================ Opinions expressed above do not necessarily -- Allan Pratt, Atari Corp. reflect those of Atari Corp. or anyone else. ...ames!atari!apratt
roeder@sbsvax.UUCP (Edgar Roeder) (01/04/90)
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 <setjmp.h> 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 '---'
apratt@atari.UUCP (Allan Pratt) (01/06/90)
In article <2009@sbsvax.UUCP>, roeder@sbsvax.UUCP (Edgar Roeder) writes
stuff about using the terminate handler.
This "hacker's approach to terminate handling" is one of the scariest
things I have ever seen. The terminate handler is meant to let you
restore state which you might have changed before you terminate. It is
not intended to let you off the terminate hook, so to speak. You
should always return normally from a terminate handler, using rts. If
you don't want ^C to stop your program, you should not use cooked
GEMDOS calls. If you don't want a bus error to stop your program, you
should install an exception handler of your own. The terminate
vector just isn't meant for these things.
Correct usage of the terminate vector includes calling BIOS to restore
the state of the screen and keyboard, un-installing vectors like timers
and interrupts you've installed, and the like. You can't make GEMDOS
calls from inside a terminate handler, or call something which might
make GEMDOS calls, like flushing output streams or even closing
workstations which are really metafiles.
I use terminate handlers in graphics programs which change the rez or
palette: the terminate handler changes back to the original rez and
palette. Doing other stuff is asking for trouble: most especially, you
will die horribly if run under MetaDOS or some future multi-tasking
TOS. (MetaDOS is a filesystem call dispatcher placed between user
programs and foreign filesystems like CD-ROMs and (eventually)
networks.)
Please, please, just use the terminate vector to clean up after yourself
and RTS, and save yourself the grief later on.
(By the way, the original article has the wrong address for the
terminate vector: it's at 0x0408, not 0x0400. The exception number,
0x0102, is right.)
============================================
Opinions expressed above do not necessarily -- Allan Pratt, Atari Corp.
reflect those of Atari Corp. or anyone else. ...ames!atari!apratt
t68@nikhefh.nikhef.nl (Jos Vermaseren) (01/09/90)
In a previous article A. Pratt comments about that the terminate vector should only be used to terminate. There are exceptions to this!!!!!!!! A good editor that runs from a good shell (a shell which has installed an emergency break, so that a child process can be stopped) should not be killable. See for instance vi under UNIX. Ctrl-C won't kill it. The problem is that Atari has no proper way to kill a child process, so kludge methods has to be made to make GEMDOS believe that the child itself asks for termination. After that there are some children that don't want to be killed. This can be done rather easily. Make a routine and install it as termvector. The whole routine reads: unlk a6 rts This causes a return from the term routine in GEMDOS before it starts cleaning up (I can hear Allan already.....). Of course it would be much better if Atari would come up with some standards on how to do this in a conventional way, together with a proper way to kill processes that don't produce output via GEMDOS. Using the environment seems to clumsy because it has to be checked before the kill command (Pterm) is given, which means that the environment has to be checked from an interrupt routine. That isn't nice. A reserved variable in the low memory globals would be better. It the variable is zero, the process may be killed. A second variable/flag could indicate whether the Atari is going to kill a process safely (for instance only when the PC points to an address inside the memory of the process). Sounds like some work for Allan to make up a good scheme. Jos Vermaseren t68@nikhefh.nikhef.nl