'===========================================================================
' Subject: Shell routine for TCB              Date: 06-14-03 (  :  )       
'  Author: Michael Webster                    Code: TBC                    
'  Origin: mfwebster@pdq.net                Packet: TOKIWA.ABC
'===========================================================================
* TBCSHELL.BAS is a TBC program that includes 
* a SHELL procedure and several related 
* procedures. 
* 
* Public Domain 2003 Michael Webster 
* :  mfwebster@pdq.net 
* This was coded for the TOKIWA 8086 BASIC 
* compiler Ver. 5.55  *** Sample ***, 
* Copyright by Genjii OKADA. 
* 
* For testing, this program was compiled with 
* TBC TBCSHELL.BAS /A /O /#X for an EXE, and 
* TBC TBCSHELL.BAS /A /C /O /#X for a COM file. 
* 
* Likely error codes for the Load and Execute 
* Program function (Interrupt 21h, AX = 4B00h): 
* 0001h     INVALID_FUNCTION 
* 0002h     FILE_NOT_FOUND 
* 0003h     PATH_NOT_FOUND 
* 0004h     TOO_MANY_OPEN_FILES 
* 0005h     ACCESS_DENIED 
* 0008h     NOT_ENOUGH_MEMORY 
* 000Ah     BAD_ENVIRONMENT 
* 000Bh     BAD_FORMAT 
* If you fail to call FreeUnusedMemory first, 
* then the function will return error code 8. 
 
* These are global because I could find no 
* reasonable way to return a string from 
* a procedure. 
g_comspec$ = "" 
g_command$ = "" 
 
* This is global because it is accessed 
* from several procedures. 
integer g_pspseg 
 
* This LOADEXEC structure is global because 
* I could not get LOC and OFFSET to work 
* correctly for local integers. 
integer g_le_env 
integer g_le_cmdoff 
integer g_le_cmdseg 
integer g_le_fcb1off 
integer g_le_fcb1seg 
integer g_le_fcb2off 
integer g_le_fcb2seg 
 
*** Test code *** 
 
call GetCommandLine 
*print g_command$ 
call FreeUnusedMemory 
call Shell(g_command$) 
*call Shell("mem /d/p") 
 
*** Procedures *** 
 
subroutine InitPspSeg 
    * Sets the global g_pspseg to the segment 
    * address of the Program Segment Prefix. 
 
    integer codeseg 
 
    /mov ax,cs 
    value = codeseg 
    #c g_pspseg = codeseg 
    #e g_pspseg = codeseg - 16 
    return 
subend 
 
subroutine InitComspec 
    * Sets the global g_comspec$ to the value 
    * of the COMSPEC environment variable. 
 
    integer n, envseg 
    character c 
 
    call InitPspSeg 
    envseg = dpeek(g_pspseg; &2c) 
    n = 0 
    while peek(envseg; n) <> 0 
        g_comspec$ = "" 
        while peek(envseg; n) <> 0 
            c = chr$(!) 
            g_comspec$ = g_comspec$ + c 
            n = n + 1 
        wend 
        if instr(g_comspec$,"COMSPEC") = 1 then 
            g_comspec$ = mid$(g_comspec$,instr(g_comspec$,"=") + 1) 
            break 
        endif 
        n = n + 1 
    wend 
    return 
subend 
 
subroutine GetCommandLine 
    * Copies the program's command line to 
    * the global g_command$. 
 
    integer i, n 
    character c 
 
    call InitPspSeg 
 
    g_command$ = "" 
 
    n = peek(g_pspseg; &80) 
    for i = &81 to n + &81 
        c = chr$(peek(g_pspseg; i)) 
        g_command$ = g_command$ + c 
    next i 
    return 
subend 
 
subroutine FreeUnusedMemory 
    * Resizes the program's memory block to 
    * free unused memory above the program. 
 
    integer stackseg, paragraphs 
 
    call InitPspSeg 
    /mov    ax,ss 
    value = stackseg 
    paragraphs = stackseg + 4096 - g_pspseg 
    /mov    bx,paragraphs 
    /mov    ax,g_pspseg 
    /mov    es,ax 
    /mov    ax,&4a00 
    /int    &21 
    /jnc    @10 
 
    * Sophisticated error handling :) 
    print   ! 
10  return 
subend 
 
subroutine Shell(command$) 
    * Starts a copy of the command processor and 
    * passes it <command$>, formatted as a proper 
    * command tail. 
 
    integer g_comspec_off 
 
    call InitPspSeg 
    call InitComspec 
 
    * This string must be initialized before 
    * initializing LOADEXEC because TBC will 
    * move it when it is initialized ! 
    cmd_tail$ = " /C " 
    cmd_tail$ = cmd_tail$ + command$ 
    cmd_tail$ = chr$(len(cmd_tail$)) + cmd_tail$ + chr$(13) 
 
    * Initialize LOADEXEC to use a copy of the 
    * parent's environment and FCBs, and the 
    * formatted command string. Note that the 
    * storage order depends on the order in 
    * which the variables are initialized rather 
    * than the order in which they are declared. 
    g_le_env = 0 
    g_le_cmdoff = dpeek(loc(cmd_tail$)) 
    /mov ax,ds 
    value = g_le_cmdseg 
    g_le_fcb1off = &5c 
    g_le_fcb1seg = g_pspseg 
    g_le_fcb2off = &6c 
    g_le_fcb1seg = g_pspseg 
 
    g_comspec_off = dpeek(loc(g_comspec$)) 
 
    /mov    dx,g_comspec_off 
    /push   ds 
    /pop    es 
    /mov    bx,offset g_le_env 
    /mov    ax,bx 
    /mov    ax,&4b00 
    /int    &21 
    /jnc    @10 
 
    * Sophisticated error handling :) 
    print ! 
10  return 
subend 
 
end 
