'===========================================================================
' Subject: GET PROGRAM STARTUP LOCATION       Date: 04-16-99 (15:55)       
'  Author: Dan Autery                         Code: ASIC                   
'  Origin: Autery@aol.com                   Packet: ASIC.ABC
'===========================================================================
REM *** PROGLOC.ASI -- Get Program Startup Location
REM *** Donated to the Public Domain by Dan Autery (autery@aol.com)
REM ***
REM *** Code based on FILESPEC.BAS, by Ronny Ong
REM ***
REM *** The following snippets show two routines for getting the startup
REM *** directory of your EXE program.  This information is handy for a
REM *** number of reasons.  In the examples below, the routines return
REM *** the path name of the data file where this program's user-
REM *** preferences are stored.  This allows the program to always find
REM *** and/or write to the correct file.
REM ***
REM *** The first routine is a plain, ASIC 5.0, adaptation of FILESPEC.BAS
REM *** The second routine is an Assembly level version of the above.
REM *** You'll find that the latter compiles to a MUCH smaller size than
REM *** the former (and is faster as well). ;)

REM *** First, find the program's DATA segment:  This GETREGS routine
REM *** _must_ be placed at the VERY beginning of your ASIC program
REM *** (immediately after any REM, DIM, or DATA statements). Placing it
REM *** later in your code could have potentially harmful results..

GETREGS(na,na,na,na,na,na,na,DS,na)
Dseg=DS

REM 'Gets the location (directory) of this program and puts it in progname$
progloc: 
 AX=&Hex6200
 REM ** Calls DOS interrupt 21 (hex), service 6200 (hex) (GET PSP)
 Int86(&hex21,AX,BX,Na,Na,Na,Na,Na,Na,Na)
 DEFSEG=BX
 num=PEEK(&hex2D)
 AB&=num
 AB&=AB&*256&
 num=PEEK(&hex2C)
 AT&=num
 AB&=AB&+AT&
 DEFSEG=AB&

 REM ** The following assembles the complete path & name:
 n=1
 progname$=""
 value=0
 WHILE n=1
   num=PEEK(value)
   value=value+1
   IF num=0 THEN
     num=PEEK(value)
     IF num=0 THEN
       value=value+3
       num=PEEK(value)
       WHILE num<>0
         value=value+1
         key$=CHR$(num)
         progname$=progname$+key$
         num=PEEK(value)
       WEND
       n=0
     ENDIF
   ENDIF
   IF value<0 THEN
     n=0
   ENDIF
 WEND

 REM ** At this point, progname$ contains the path\name of your program
 REM ** Since ASIC strings can only be 79 bytes, though, let's double-check
 REM ** to make sure our progname$ is valid:
 key$=FIND FIRST(progname$,0)
 IF ERROR=0 THEN
   n=LEN(progname$)
 ENDIF

 REM ** This next routine finds the length of our string, minus the name
 REM ** of the EXE file (to allow us to extract the directory name).
 num=0
 IF n>0 THEN
   WHILE n>0
     n=n-1
     key$=MID$(progname$,n,1)
     IF key$="\" THEN
       num=n
       n=n-1
       key$=MID$(progname$,n,1)

 REM ** While the following ~might~ not be necessary, it's here to trap
 REM ** any odd startup strings.  For example, if a person were to start
 REM ** your program from one directory up by typing "..\prog.exe",
 REM ** progname$ could be: "c:\mydir\..\prog.exe".
 REM ** If found, this adjusts the string length to compensate.
       IF key$<>"." THEN
         n=0
       ENDIF
     ENDIF
   WEND
 ENDIF

 REM ** Again, we're checking the length of our string because of the
 REM ** ASIC '79 character' string limitation (grrr!). Since the DAT file
 REM ** name is 8 characters, the path can be no longer than 71 chars.
 IF num>71 THEN
   num=0
 ENDIF
 progname$=LEFT$(progname$,num)
 progname$=progname$+"prefs.dat"
 DEFSEG=-1
RETURN


REM 'This is the Assembly level (machine language) version of the above.
REM 'The actual Assembly commands used to create this are shown below.
progloc_asm:
 DI=VARPTR(progname$)
 ES=Dseg
 CODE 30,6
 SETREGS(na,na,na,na,DI,na,na,na,ES)
 CODE 6,87,49,192,170,180,98,205,33,142,219,190,44,0,173,142,192,6,31,49
 CODE 255,185,0,128,49,192,242,174,117,54,174,117,249,71,71,185,79,0,137,203
 CODE 87,94,242,174,117,38,41,203,137,217,95,7,6,87,243,164,253,137,217,176
 CODE 92,242,174,87,79,184,46,46,175,95,117,6,176,92,242,174,242,174,252
 CODE 71,71,176,0,170,95,7,7,31

 REM 'progname$ now contains the complete path (minus the exe name)
 REM 'of your program.

 num=LEN(progname$)
 IF num>71 THEN
   progname$=""
 ENDIF
 progname$=progname$+"prefs.dat"
RETURN

REM  QB CALL ABS CODES   ASM CODES         ASIC CODES   NOTES
REM (&H1E)              ' PUSH DS             30
REM (&H6)               ' PUSH ES             6
REM           *** USE SETREGS(na,na,na,na,DI,na,na,na,ES) HERE ***
REM (&H6)               ' PUSH ES             6         save (new) ES
REM (&H57)              ' PUSH DI             87        save (new) DI
REM (&H31)+(&HC0)       ' XOR AX,AX           49,192    AX=0
REM (&HAA)              ' STOSB               170       nul progname$ string
REM (&HB4)+(&H62)       ' MOV AH,62           180,98    AX=6200h
REM (&HCD)+(&H21)       ' INT 21              205,33    call the interrupt
REM (&H8E)+(&HDB)       ' MOV DS,BX           142,219   DS=PSP
REM (&HBE)+(&H2C)+(&H0) ' MOV SI,002C         190,44,0  SI=2Ch
REM (&HAD)              ' LODSW               173       Get the entire word
REM (&H8E)+(&HC0)       ' MOV ES,AX           142,192   and put it into ES
REM (&H6)               ' PUSH ES             6         Copy ES to DS (for later)
REM (&H1F)              ' POP DS              31        ..via the stack
REM (&H31)+(&HFF)       ' XOR DI,DI           49,255    DI=0
REM (&HB9)+(&H0)+(&H80) ' MOV CX,8000         185,0,128 CX=32K (max env size)
REM (&H31)+(&HC0)       ' XOR AX,AX           49,192    AX=0
REM (&HF2)              ' Again: REPNZ        242       repeat while AL is
REM (&HAE)              ' SCASB               174       NOT equal to ES:DI
REM (&H75)+(&H36)       ' JNZ Exit            117,54    If No match, then exit
REM (&HAE)              ' SCASB               174       Is next byte zero?
REM (&H75)+(&HF9)       ' JNZ Again           117,249   No? Repeat search.
REM (&H47)              ' INC DI              71        skip two bytes
REM (&H47)              ' INC DI              71        after the '00'
REM (&HB9)+(&H4F)+(&H0) ' MOV CX,004F         185,79,0  Max string len=79
REM (&H89)+(&HCB)       ' MOV BX,CX           137,203   Copy to BX for later
REM (&H57)              ' PUSH DI             87        Copy DI to SI
REM (&H5E)              ' POP SI              94        ..via the stack
REM (&HF2)              ' REPNZ               242       Searching for nul terminator
REM (&HAE)              ' SCASB               174       Do the search
REM (&H75)+(&H26)       ' JNZ Exit            117,38    No match? Then exit.
REM (&H29)+(&HCB)       ' SUB BX,CX           41,203    Get our string size
REM (&H89)+(&HD9)       ' MOV CX,BX           137,217   And put it in CX
REM (&H5F)              ' POP DI              95        Restore string pointer
REM (&H7)               ' POP ES              7         And program segment
REM (&H6)               ' PUSH ES             6         Save 'em (needed, since
REM (&H57)              ' PUSH DI             87        we're popping them later)
REM (&HF3)              ' REPZ                243       Copy 'CX' bytes
REM (&HA4)              ' MOVSB               164       From DS:SI to ES:DI
REM (&HFD)              ' STD                 253       Set direction flag for backward scan
REM (&H89)+(&HD9)       ' MOV CX,BX           137,217   Restore string size
REM (&HB0)+(&H5C)       ' MOV AL,5C           176,92    Looking for "\"
REM (&HF2)              ' REPNZ               242       Repeat while ES:DI
REM (&HAE)              ' SCASB               174       is NOT equal to AL
REM (&H57)              ' PUSH DI             87        Save DI
REM (&H4F)              ' DEC DI              79        Odd, but needed
REM (&HB8)+(&H2E)+(&H2E)' MOV AX,2E2E         184,46,46 Looking for ".."
REM (&HAF)              ' SCASW               175       in the next word
REM (&H5F)              ' POP DI              95        Restore DI (w/out changing flags)
REM (&H75)+(&H6)        ' JNZ Over            117,6     No match?  Jump over:
REM (&HB0)+(&H5C)       ' MOV AL,5C           176,92    Look for next '\'
REM (&HF2)              ' REPNZ               242       Do twice: Once to
REM (&HAE)              ' SCASB               174       remove "..", and once
REM (&HF2)              ' REPNZ               242       to remove extra dir
REM (&HAE)              ' SCASB               174       name that ".." adds
REM (&HFC)              ' Over: CLD           252       ALWAYS Clear dir flag if set
REM (&H47)              ' INC DI              71        SCASB left us one byte
REM (&H47)              ' INC DI              71        before the '\', but we
REM (&HB0)+(&H0)        ' MOV AL,00           176,0     need one byte after..
REM (&HAA)              ' STOSB               170       write end-of-string nul
REM (&H5F)              ' Exit: POP DI        95        restore the stack
REM (&H7)               ' POP ES              7                "
REM (&H7)               ' POP ES              7                "
REM (&H1F)              ' POP DS              31               "
