[comp.sys.atari.st] How to determine if a pgm is called from the desktop

VBRANDT@DBNUAMA1.BITNET (11/24/89)

Hello all,

   ... in an effort to return the Atari16 digests to discussions of a more
technical nature, I'd like to report on my findings on how to determine whether
a program was called from he desktop or from another program, such as a shell.

   I wanted to be able to use a construct like "if (Desktop()) Cconin();" to
enable desktop users to see messages from my program without forcing shell
users to press a key each time the program terminates.  The function should be
as general as possible (ie. work with all TOS versions).

   From older discussions and some hints in personal email, three
possible ways to do this emerged:

1. The desktop puts an unneccessary CR (0x0d) character at the end of the
   command line when a progam is invoked from the desktop.  If one finds such
   a CR stuck to the end of the command line, one can assume to have been
   called from the desktop.

2. Under the assumption that all shells set the PATH environment, one can test
   for existence of this variable, thus being certain to have been called by
   a shell.

3. If one finds the location of the parent process to be in ROM, the program
   was called by the desktop.

   Method 1 fails if a shell (or other program, for that matter) tries to mimic
the desktop too closely, or if someone@atari finally decides to get rid of such
obsolete <beep> in future revisions of TOS.

   Method 2 fails if a program is simply being called by another program, or if
one has an auto-folder environment-setting utility (or the mythical env-set acc
by Atari :-).  One could argue that I could distinguish between shells and nor-
mal programs in this way, but (i) once a PATH entry is in the environment, it
is passed down to all p_exec'ed processes, and (ii) it is the responsibility
of the CALLING program to ensure that a p_exec'ed program can display its
messages properly (my personal opinion).

   Method 3, however, proved workable.  Here are some source examples, with a
description following.  I have included ready-to-assemble versions for both
CCD/ICD ST-Pascal and Borland Turbo C, for the benefit of those who don't want
to delve into 68k assembler and still want to use the routine.

   Der Worte sind genug gewechselt, lasst uns endlich Daten sehn:
(famous German quote)

=== DESK-PAS.S ================================================================
        .title  'Desktop Function for PASCAL'
        .globl  basepage,DESKTOP

* DESKTOP.S     --      Last change: 19-Nov-89 VAB
*
* PASCAL declaration:  function Desktop: boolean; external;

p_tbase  =    8         ; basepage offset to text segment pointer
p_parent =   36         ; basepage offset to parent basepage segment pointer

_sysbase = $4f2         ; -> base of OS

os_base  =    8         ; os_header offset: -> base of OS
os_magic =   20         ; os_header offset: -> end of OS

Super    =   32         ; GEMDOS Super(ssp)
GEMDOS   =    1         ; GEMDOS trap number

* The following external symbol MUST be all uppercase

DESKTOP:move.l  basepage,a5             ; A5 -> my basepage
        move.l  p_parent(a5),a5         ; A5 -> parent basepage
        move.l  p_tbase(a5),a5          ; A5 -> text segment of parent process

        clr.l   -(sp)                   ; go into Supervisor mode, Atari style
        move.w  #Super,-(sp)
        trap    #GEMDOS
        addq.l  #6,sp
        move.l  d0,-(sp)
        move.w  #Super,-(sp)

        move.l  _sysbase,a4             ; A4 -> OS header
        move.l  os_base(a4),a4          ; find the 'real' copy of the OS header
        move.l  os_magic(a4),a3         ; A3 -> end of OS

        trap    #GEMDOS                 ; back to user mode
        addq.l  #6,sp

        cmp.l   a5,a4                   ; address below OS start?
        bpl.s   ret_0                   ; yes: return <false>
        cmp.l   a5,a3                   ; address above OS start?
        bmi.s   ret_0                   ; yes: return <false>
        moveq.l #-1,d0                  ; no: return <true>
        rts

ret_0:  moveq.l #0,d0                   ; return <false>
        rts
        end
=== End of DESK-PAS.S =========================================================

   And now the same thing for Turbo C:

=== DESK-TC.S =================================================================

        .title  'Desktop() Function for Turbo C'
        .globl  _BasPag,Desktop

* DESKTOP.S     --      Last change: 19-Nov-89 VAB

p_tbase  =    8         ; basepage offset to text segment pointer
p_parent =   36         ; basepage offset to parent basepage segment pointer

_sysbase = $4f2         ; -> base of OS

os_base  =    8         ; os_header offset: -> base of OS
os_magic =   20         ; os_header offset: -> end of OS

Super    =   32         ; GEMDOS Super(ssp)
GEMDOS   =    1         ; GEMDOS trap number

Desktop:movem.l a3-a5,-(sp)             ; save work registers
        move.l  _BasPag,a5              ; A5 -> my basepage
        move.l  p_parent(a5),a5         ; A5 -> parent basepage
        move.l  p_tbase(a5),a5          ; A5 -> text segment of parent process

        clr.l   -(sp)                   ; go into Supervisor mode, Atari style
        move.w  #Super,-(sp)
        trap    #GEMDOS
        addq.l  #6,sp
        move.l  d0,-(sp)
        move.w  #Super,-(sp)

        move.l  _sysbase,a4             ; A4 -> OS header
        move.l  os_base(a4),a4          ; find the 'real' copy of the OS header
        move.l  os_magic(a4),a3         ; A4 -> end of OS

        trap    #GEMDOS                 ; back to user mode
        addq.l  #6,sp

        cmp.l   a5,a4                   ; address below OS start?
        bpl.s   ret_0                   ; yes: return <false>
        cmp.l   a5,a3                   ; address above OS start?
        bmi.s   ret_0                   ; yes: return <false>
        moveq.l #-1,d0                  ; no: return <true>
        bra.s   return

ret_0:  moveq.l #0,d0                   ; return <false>
return: movem.l (sp)+,a3-a5             ; restore work registers
        rts
        end
=== End of DESK-TC.S ==========================================================

   Now for the explanation:

   First I get a pointer to the basepage of the parent process.  Fortunately
this is easy for both ST-PASCAL and Turbo C, since both compilers have a global
basepage pointer that is filled in by the startup code.  The Turbo C one is
called _BasPag and documented, while the PASCAL one is called basepage and
is undocumented.

   There is a way to get such a pointer if you don't have one, but it's a
little more complicated. We'll look at it in next tuesdays lecture. :-)

   Now we want our subroutine to work with ROM and RAM versions, so we have to
compare the parent process text segment pointer with the start and end of the
operating system.  There are ROM and RAM versions of TOS, and some RAM versions
sit in low memory while others sit in high memory (for those who don't believe
there are TOSes in high mem, I have one. It's a freely relocatable TOS 1.4 :-)

   The parent process text segment pointer is easy: we just pluck it from our
daddies basepage.

   The start and end addresses of the OS are somewhat more complicated.  The
start address is found in the os_header, which is pointed to by the documented
variable _sysbase.  We have to use one indirection, since some hard disk driver
software sometimes overwrites some of the RAM copy of the os_header.  Since the
'real' header always points to itself anyway, it doesn't hurt.  The end address
of the OS is the real problem.  There is no documented way to get at that, as
opposed to the end of the OS *variables*.  It turns out that the os_magic is
always located at the end of the OS.  While this is not documented, this holds
true for all TOSes up to 1.4, the first ST/E TOS 1.6, and the TT TOS 030.

   Big question to Atari:  Can I rely on the fact that the os_magic entry in
the OS header always points to an address at the end of TOS?  If not, how do
I get the adress of the end of the OS text segment?  (I don't want to trust
the text segment length data in the parent process basepage.)

   With the last assumption, the rest is easy. See sample above.  The code
fragment should work with all compilers if the result return register is
adjusted.  As you can see, there's not really much of a difference between
the PASCAL and TC versions: the basepage pointer is called differently, and
TC needs some registers saved.

   Moral:  Please, let's all use the method descrived above to make life
easier for people who sometimes use the desktop and sometimes use a shell
(like me :-)

   That's it.  C&CC (comments and constructive criticism) are invited.

----------------------------------------------------------------------------
Bitnet:  VBRANDT@DBNUAMA1 (will go away some day ...)  Volker A. Brandt
          UNM409@DBNRHRZ1 (alternative)                Angewandte Mathematik
UUCP:    ...!unido!DBNUAMA1.bitnet!vbrandt             (Bonn, West Germany)
ARPAnet: VBRANDT%DBNUAMA1.BITNET@CUNYVM.CUNY.EDU