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