[comp.sys.atari.st] <Control> C and autostarting GEM programs

Gribnif@UMass.BITNET (Dan Wilga at UMASS Amherst) (02/02/88)

Greetings,
     I realize there have probably been atleast two buzillion (okay, I'm
exaggerating, one buzillion) questions relating to TOS' handling of the
<Control> C key and ways of disabling it, but I'm sure we could all use
a refresher course by now... :-)
    I have found that using the XBIOS (or are they BIOS??) functions
Cconws/Cconout to send information to the console is actually quite nice
for the application I am writing, since they allow the user to use
<Control> Q and <Control> S for flow control of the data on the screen
(whereas the "raw" output functions beginning with "B" do not.) The problem
is that as soon as the user enters a <Control> C the program is immediately
terminated with a status -32. Not nice.
     So it all boils down to this: has anyone found a LEGAL way (short of
redefining the keyboard, assuming TOS is looking for the ASCII and not the
scan code--redefining it would be inefficient for me anyway) of disabling
or trapping the key so that the program is not terminated? Moshe?
     Second of all I would like to know how programs like GEMSTART work
and if they can, in fact, work with the new ROMs without modification
(I have heard that the original GEMSTART won't work with the new ROMs.)
If you want to try something rather humorous, write a DA that Pexec's a
GEM program as soon as it runs. The application runs normally, puts up its
menu bar and all, but as soon as it does an evnt_multi the desktop magically
takes-over control once again which, of course, leads to several bombs if
you get tired of watching disk drive windows pop up from nowhere when you
click on the desktop and decide to select a menu item.
     Thanks in advance!

---------------------------------------------------------------------------
EEEeek! I went against my own                  Dan Wilga
credo and used a :-) !!!                       AndOr@UMASS.Bitnet
I'm so ashamed...                              Arpa? Just try and figure it
                                                 out!

dclemans@mntgfx.mentor.com (Dave Clemans) (02/04/88)

Re: Control C

GEMDOS doesn't directly terminate a process when it sees a Control-C character
in a "cooked" input function; instead it calls a special pseudo-interrupt
vector called the critical exception handler.  The default code for that
handler is what terminates the process.  If you install a replacement for it
you can do whatever you want.  If I remember correctly this is described
in the Atari GEMDOS Users Guide manual.

Re: autostarting GEM programs

My understanding is that what the autostart programs do is to set up a
small TSR routine attached to the vertical blank interrupt vector.  On each
interrupt the code looks around and tries to guess whether or not GEM is
initialized yet.  If it looks initialized it disengages from the vertical
blank interrupt and starts the requested GEM program.  Whether or not a
specific version of this technique works with all versions of the ROM's
depends on specifically how the code checks for GEM being initialized.

dgc

apratt@atari.UUCP (Allan Pratt) (02/05/88)

in article <1988Feb3.080736.14303@mntgfx.mentor.com>, 
dclemans@mntgfx.mentor.com (Dave Clemans) says:

> Re: Control C

> GEMDOS doesn't directly terminate a process when it sees a Control-C character
> in a "cooked" input function; instead it calls a special pseudo-interrupt
> vector called the critical exception handler.  The default code for that
> handler is what terminates the process.  If you install a replacement for it
> you can do whatever you want.  If I remember correctly this is described
> in the Atari GEMDOS Users Guide manual.

Not exactly.  First, GEMDOS calls the "terminate vector" not the
"critical exception handler." Second, that vector points by default an
RTS instruction.  It is the code after the call which terminates the
process. 

This vector gets called when you use Pterm(), too: it's not strictly a
"control C" handler. 

You can avoid being terminated by doing a "setjmp(term_buf)" where you
want to recover, and setting this vector to point to something like
"longjmp(term_buf,1)"

Unfortunately, there isn't a lot of documentation on this, and other
projects prevent me from going into more detail.

============================================
Opinions expressed above do not necessarily	-- Allan Pratt, Atari Corp.
reflect those of Atari Corp. or anyone else.	  ...ames!atari!apratt

tainter@ihlpg.ATT.COM (Tainter) (02/12/88)

In article <971@atari.UUCP>, apratt@atari.UUCP (Allan Pratt) writes:
> in article <1988Feb3.080736.14303@mntgfx.mentor.com>, 
> dclemans@mntgfx.mentor.com (Dave Clemans) says:
> > Re: Control C
> Not exactly.  First, GEMDOS calls the "terminate vector" not the
> "critical exception handler." Second, that vector points by default an
> RTS instruction.  It is the code after the call which terminates the
> process. 

> This vector gets called when you use Pterm(), too: it's not strictly a
> "control C" handler. 

This last bit makes it worthless, unless you can tell us how to tell if
we are getting a ^C or a Pterm().  Of course, ^C still isn't worth much
even so since it is not an interrupt but a polled event (polled by calls
to the gemdos cooked routines) but it would be worth something then.

> Opinions expressed above do not necessarily	-- Allan Pratt, Atari Corp.

--j.a.tainter

apratt@atari.UUCP (Allan Pratt) (02/17/88)

in article <4803@ihlpg.ATT.COM>, tainter@ihlpg.ATT.COM (Tainter) says:
>> This vector gets called when you use Pterm(), too: it's not strictly a
>> "control C" handler. 
> 
> This last bit makes it worthless, unless you can tell us how to tell if
> we are getting a ^C or a Pterm().
> --j.a.tainter

You're getting a Pterm if you call Pterm, and you're getting ^C if you
don't.  If you know you're calling Pterm, then call your clean-up
routine on your own and un-install it before you terminate.

Also un-install your terminate vector before you Pexec: you can tell
what the result of the Pexec was by the return code: -32L means the
child terminated with ^C.  Any other LONG negative value means the
Pexec failed, and any LONG positive value (i.e. (x & 0xffff0000) == 0)
means it called Pterm(x).

Please don't shoot words like "worthless" around unqualified.

============================================
Opinions expressed above do not necessarily	-- Allan Pratt, Atari Corp.
reflect those of Atari Corp. or anyone else.	  ...ames!atari!apratt

david@bdt.UUCP (David Beckemeyer) (02/19/88)

In article <980@atari.UUCP> apratt@atari.UUCP (Allan Pratt) writes:
>You're getting a Pterm if you call Pterm, and you're getting ^C if you
>don't.  If you know you're calling Pterm, then call your clean-up
>routine on your own and un-install it before you terminate.
>
This isn't entirely true.  An exception can also produce a Pterm. So
an exception would look like a ^C, based on the above test.  The only
way around this is to install your own exception handlers, which isn't
too clean either, if you ask me (but who did, right?).
-- 
David Beckemeyer			| "To understand ranch lingo all yuh
Beckemeyer Development Tools		| have to do is to know in advance what
478 Santa Clara Ave, Oakland, CA 94610	| the other feller means an' then pay
UUCP: ...!ihnp4!hoptoad!bdt!david 	| no attention to what he says"

apratt@atari.UUCP (Allan Pratt) (02/23/88)

in article <142@bdt.UUCP>, david@bdt.UUCP (David Beckemeyer) says:
> 
> In article <980@atari.UUCP> apratt@atari.UUCP (Allan Pratt) writes:
>>You're getting a Pterm if you call Pterm, and you're getting ^C if you
>>don't.  If you know you're calling Pterm, then call your clean-up
>>routine on your own and un-install it before you terminate.
>>
> This isn't entirely true.  An exception can also produce a Pterm.

Sorry, of course that's right.  The bomb handler does a Pterm to try to
kill the offending process.  But why would you want to handle bombs any
differently from ^C? (Besides, correct programs don't produce bombs! :-)

============================================
Opinions expressed above do not necessarily	-- Allan Pratt, Atari Corp.
reflect those of Atari Corp. or anyone else.	  ...ames!atari!apratt

steven@cwi.nl (Steven Pemberton) (02/26/88)

In article <986@atari.UUCP> apratt@atari.UUCP (Allan Pratt) writes:
> But why would you want to handle bombs any differently from ^C?
> (Besides, correct programs don't produce bombs! :-)

Well, if you're writing an interpreter for a programming language, you
want ^C to interrupt the running interpreted program, but not the
interpreter itself. On the other hand, you want a bomb to kill the
interpreter.

Steven Pemberton, CWI, Amsterdam; steven@cwi.nl

apratt@atari.UUCP (Allan Pratt) (03/01/88)

From article <217@piring.cwi.nl>, by steven@cwi.nl (Steven Pemberton):
> In article <986@atari.UUCP> apratt@atari.UUCP (Allan Pratt) writes:
>> But why would you want to handle bombs any differently from ^C?
>> (Besides, correct programs don't produce bombs! :-)
> 
> Well, if you're writing an interpreter for a programming language, you
> want ^C to interrupt the running interpreted program, but not the
> interpreter itself. On the other hand, you want a bomb to kill the
> interpreter.
> 
> Steven Pemberton, CWI, Amsterdam; steven@cwi.nl

No you don't... You want a bomb to kill the running program, back to
the interpreter, so the programmer can correct the problem.  Or,
you should install your own bomb handler (as most interpreters better
than ST BASIC do).

============================================
Opinions expressed above do not necessarily	-- Allan Pratt, Atari Corp.
reflect those of Atari Corp. or anyone else.	  ...ames!atari!apratt

david@bdt.UUCP (David Beckemeyer) (03/04/88)

Since this discussion hasn't ended yet, I thought I'd bring up a few
gotcha's regarding writing your own ^C and bomb handlers.

GEMDOS calls the Pterm handler with all files open, and it could
happen at any instant in the code.  Consider a recursive File Tree
scanner that uses Fsetdta() and Fsfirst/Fsnext:


scan(pattern, mask)
char *patter;
int mask;
{
	DMABUFFER dbuf, *olddma;

	/* remember callers DTA buffer */
	olddma = Fgetdta();
	/* Set DTA buffer for this scan */
	Fsetdma(dbuf);
	if (Fsfirst(pattern, mask) == 0) {
		do {
			/* see if found a directory */
			if (isdirectory(dbuf))
				scan(pattern, mask);
			else
				/* process file */
		} while (Fsnext() == 0);
	}
	/* reset DTA */
	Fsetdta(olddma);
}

If a bomb or ^C occurs at recursion level N, the dta pointer points
to memory on the stack (the auto dbuf variable). Clearly there are
potential problems here.  What happens to the stack?  How can the
handler fix-up the stack?  A long-jump?  But then what happens in
the above case where now the DTA points to a place that will be used
again for new stack space.

Open files also remain open when the Pterm is called, so these must
also be cleaned up.

My point is: don't be mislead into thinking that a Pterm handler is
nothing more than a simple longjump.
	
	
	
-- 
David Beckemeyer			| "To understand ranch lingo all yuh
Beckemeyer Development Tools		| have to do is to know in advance what
478 Santa Clara Ave, Oakland, CA 94610	| the other feller means an' then pay
UUCP: ...!ihnp4!hoptoad!bdt!david 	| no attention to what he says"

apratt@atari.UUCP (Allan Pratt) (03/08/88)

From article <167@bdt.UUCP>, by david@bdt.UUCP (David Beckemeyer):
> What happens to the stack?  How can the
> handler fix-up the stack?  A long-jump?  But then what happens in
> the above case where now the DTA points to a place that will be used
> again for new stack space.
> 
> Open files also remain open when the Pterm is called, so these must
> also be cleaned up.
> 
> My point is: don't be mislead into thinking that a Pterm handler is
> nothing more than a simple longjump.

It *is* just a simple longjmp! (Except that it gets executed in Super
mode -- but the handler can just consist of a longjmp.) If you have a
global pointer (like an extern or your DTA) pointing into your stack,
and you do a longjmp, it still points to your stack (or where the stack
will someday be).  It's up to you not to use those pointers -- in the
case where the ^C might occur while the DTA is on the stack, your
Pterm-recovery routine should reset the DTA to somewhere else. 

On the other hand, since the DTA is only used for Fsfirst/Fsnext, you
should explicitly set it before each round of calls.  Then you're sure
to be safe from DTA fever. 

Other globals have always had that problem, though: if you longjmp,
you have to be sure you reinitialize them.

============================================
Opinions expressed above do not necessarily	-- Allan Pratt, Atari Corp.
reflect those of Atari Corp. or anyone else.	  ...ames!atari!apratt

david@bdt.UUCP (David Beckemeyer) (03/15/88)

In article <1008@atari.UUCP> apratt@atari.UUCP (Allan Pratt) writes:
>It *is* just a simple longjmp! (Except that it gets executed in Super
>mode -- but the handler can just consist of a longjmp.) If you have a...

If the Pterm handler consists of just a longjmp, doesn't that mean that the
supervisor stack is permanently shrinking, and that the program thereafter
runs in supervisor mode, even if it was started in user mode?   Wouldn't
that mean that a lot (maybe not so many) ^C's would use up all the supervisor
stack?

Also what about open files?  SOmebody has to take care of them don't they?
If the program re-opens the same files, things could get messy (one problem
is the great duplicate file name thing).

>Other globals have always had that problem, though: if you longjmp,
>you have to be sure you reinitialize them.

This statement alone (along with others in your message) suggest that
the Pterm handler is more than just a longjmp.  I still contend that
the Pterm handler must "clean up whatever needs cleaning up, and then
longjmp".   The list of "whatever needs cleaning up" is where things
can get complicated (potentially).
-- 
David Beckemeyer			| "To understand ranch lingo all yuh
Beckemeyer Development Tools		| have to do is to know in advance what
478 Santa Clara Ave, Oakland, CA 94610	| the other feller means an' then pay
UUCP: ...!ihnp4!hoptoad!bdt!david 	| no attention to what he says"

apratt@atari.UUCP (Allan Pratt) (03/18/88)

From article <176@bdt.UUCP>, by david@bdt.UUCP (David Beckemeyer):
> In article <1008@atari.UUCP> apratt@atari.UUCP (Allan Pratt) writes:
>>It *is* just a simple longjmp! (Except that it gets executed in Super
>>mode -- but the handler can just consist of a longjmp.) If you have a...
> 
> If the Pterm handler consists of just a longjmp, doesn't that mean that the
> supervisor stack is permanently shrinking, and that the program thereafter
> runs in supervisor mode, even if it was started in user mode?   Wouldn't
> that mean that a lot (maybe not so many) ^C's would use up all the supervisor
> stack?

You misunderstand me.  By "Pterm handler" I meant the thing YOU might
install to catch bombs, Pterm's, and ^C's yourself.  *Of course* Pterm
is more complicated than that in Gemdos: it has to free your memory,
close your files, and so on.

If you want to execute some procedure (like removing your vblank routine)
when you get ^C, bombs, or Pterm, you should install the address of that
procedure as the terminate vector.  When Pterm gets hit (for any of these
reasons), your handler will get called (via jsr, in super mode), and
when it returns, the Pterm continues and terminates the process.  This
is what Sid does when it says "Bye!" -- that's the terminate handler
talking.

If you are a shell and you want to return to your command prompt when
the user hits ^C, you have to be a little more clever:  you make the
terminate vector point to a routine which does a go-to-user-mode-and-longjmp
operation.  The target of the longjmp is responsible for recovering from
the problem.  Simply put, it could look like this:

#include <osbind.h>
#include <longjmp.h>
jmp_buf err_buf;

long old_terminate_value;

main_loop()
{
	char cmdbuf[258];
	int terminate_handler();

	old_terminate_value = Setexc(0x102,terminate_handler);

	if (setjmp(err_buf)) {
		/* this code is target of longjmp */
		Cconws("*Break*\r\n");
	}

	/* normal flow and longjmp both end up here */

	cmdbuf[0] = 255;
	while (1) {
		Cconws(prompt_string);
		Cconrs(cmdbuf);
		cmdbuf[cmdbuf[1]+2] = 0;  /* null-terminate the string */
		do_cmdline(&cmdbuf[2]);
	}
}

terminate_handler()
{
	longjmp(err_buf,-1);
}

exit_command()
{
	Setexc(0x102,old_terminate_value);
	Pterm(0);
}


The above fragments assume that setjmp saves the SR of the caller and
restores it for the longjmp... This is a nontrivial thing to do because
you can't just use RTE from user mode.

The exit_command fragment is here to show that you have to UN-install the
vector before you Pterm, because otherwise it'll just get hit again.

Does this make anything more clear?

============================================
Opinions expressed above do not necessarily	-- Allan Pratt, Atari Corp.
reflect those of Atari Corp. or anyone else.	  ...ames!atari!apratt

david@bdt.UUCP (David Beckemeyer) (03/22/88)

In article <1018@atari.UUCP> apratt@atari.UUCP (Allan Pratt) writes:
>You misunderstand me.  By "Pterm handler" I meant the thing YOU might
>install to catch bombs, Pterm's, and ^C's yourself.  *Of course* Pterm
>is more complicated than that in Gemdos: it has to free your memory,
>close your files, and so on.

No you misunderstand me.   I know exactly what a Pterm handler is.
I posted my original message not to help you, Allan, (I know you
know what they should do), but to help others.

Somebody originally stated that the Pterm (^C 0x102 vector) could
just be a longjmp.  And all I was trying to say was that it was a little
bit more than this, and gave a definition of "clean up and then longjmp".

Then you posted an excellent example of a Pterm/^C handler that demonstrates
how you do exactly that (clean up and then longjmp).  Where "clean up" 
depends on the application, and may be "null" in the degenerate case.

The whole thing became a much longer discussion that it ever should have been.
It's my fault for saying something, and not doing what you did and just give
a good example.  But to try and make my point one more time.  All I'm trying
to say is that there *are* cases, depending on the state of the application,
where a simple longjmp, and no "clean up" (whether handled by the Pterm handler
itself, or its target) will result in an upset system.
-- 
David Beckemeyer			| "To understand ranch lingo all yuh
Beckemeyer Development Tools		| have to do is to know in advance what
478 Santa Clara Ave, Oakland, CA 94610	| the other feller means an' then pay
UUCP: ...!ihnp4!hoptoad!bdt!david 	| no attention to what he says"