home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / PROGRAMS / UTILS / DOS_HELP / ADVMSDOS.ZIP / SHELL.ASM < prev    next >
Encoding:
Assembly Source File  |  1986-01-01  |  9.9 KB  |  330 lines

  1.     name      shell    
  2.     page      55,132
  3.     title     'SHELL.ASM -- simple MS-DOS shell'
  4. ;
  5. ; SHELL.ASM    a simple user-extendable command interpreter
  6. ;        for MS-DOS 2.X or 3.X
  7. ;
  8. ; Copyright (C) 1985 by Ray Duncan
  9. ;
  10. ; To assemble and link this program into the executable SHELL.EXE:
  11. ;        
  12. ;        C>MASM SHELL;
  13. ;        C>LINK SHELL;
  14. ;
  15.  
  16. stdin    equ    0            ;Standard Input Device
  17. stdout    equ    1            ;Standard Output Device    
  18. stderr    equ    2            ;Standard Error Device
  19.  
  20. cr    equ    0dh             ;ASCII carriage return
  21. lf      equ     0ah               ;ASCII line feed
  22. blank    equ    20h            ;ASCII blank code
  23. esc    equ    01bh            ;ASCII escape code
  24.  
  25. cseg     segment    para public 'CODE'
  26.  
  27.     assume     cs:cseg,ds:data,ss:stack
  28.  
  29. shell    proc    far            ;at entry DS = ES = PSP
  30.     mov    ax,data            ;let DS point to our data segment
  31.     mov    ds,ax
  32.     mov    ax,es:[002ch]        ;get segment of Environment Block
  33.     mov    env_seg,ax        ;from PSP and save it.
  34.                     ;now release unneeded memory...
  35.     mov      bx,100h           ;ES=segment PSP, BX=paragraphs needed
  36.         mov     ah,4ah            ;Function 4AH = modify memory block
  37.         int     21h            ;transfer to DOS
  38.     jnc    shell1            ;jump if request successful
  39.     mov    dx,offset msg1        ;otherwise display error message
  40.     mov    cx,msg1_length        ;and exit
  41.     jmp    shell4
  42. shell1:    call    get_comspec        ;get file spec for COMMAND.COM
  43.     jnc    shell2            ;jump if it was found ok 
  44.     mov    dx,offset msg3        ;COMSPEC variable not found in
  45.     mov    cx,msg3_length        ;Environment block, print error
  46.     jmp    shell4            ;message and exit
  47. shell2:    mov    dx,offset shell3    ;set Control-C vector (Int 23H)
  48.     mov    ax,cs            ;so that shell can keep control
  49.     mov    ds,ax            ;DS:DX = addr of Control-C handler
  50.     mov    ax,2523H        ;Function 25H = Set Interrupt
  51.     int    21h
  52.     mov    ax,data            ;make DS and ES point to our
  53.     mov    ds,ax            ;data segment again
  54.     mov    es,ax
  55. shell3:                    ;main loop of command interpreter
  56.     call    get_cmd            ;get a command from user
  57.     call    intrinsic        ;check if intrinsic function
  58.     jnc    shell3            ;yes, it was processed
  59.     call    extrinsic        ;no, pass it to COMMAND.COM 
  60.     jmp    shell3            ;then get another command
  61. shell4:                 ;come here if error detected, with
  62.                     ;DS:DX=message addr, CX=length
  63.     mov    bx,stderr        ;print error message on 
  64.     mov    ah,40h            ;Standard Error Device
  65.     int    21h
  66.     mov    ax,4c01h        ;exit to DOS with return code = 1
  67.     int    21h            ;indicating an error condition
  68. shell    endp
  69.  
  70.                                      ;these variables must be in Code Seg
  71. stk_seg    dw    0                 ;original SS contents
  72. stk_ptr dw      0                 ;original SP contents
  73.  
  74.  
  75. intrinsic proc    near            ;Decode a user command against
  76.                     ;the table "COMMANDS".  If match
  77.                     ;found, run the subroutine, and
  78.                     ;return Carry = False.  If no match,
  79.                     ;return Carry = True.
  80.     mov    si,offset commands    ;let SI = start of command table
  81. intr1:    cmp    byte ptr [si],0        ;is command table exhausted?
  82.     je    intr7            ;jump, end of table found
  83.     mov    di,offset inp_buf    ;no, let DI = addr of user input
  84. intr2:    cmp    byte ptr [di],blank    ;scan off any leading blanks 
  85.     jne    intr3
  86.     inc    di
  87.     jmp    intr2
  88. intr3:    mov    al,[si]            ;get next command char
  89.     or    al,al            ;check if end of string
  90.     jz    intr4            ;jump, entire string matched
  91.     cmp    al,[di]            ;compare to next input char
  92.     jnz    intr6            ;jump, found mismatch
  93.     inc    si            ;go to next char in strings
  94.     inc    di
  95.     jmp    intr3
  96. intr4:    cmp    byte ptr [di],cr    ;make sure user's command
  97.     je    intr5            ;is the same length, next char
  98.     cmp    byte ptr [di],blank    ;must be blank or Return
  99.     jne    intr6
  100. intr5:    call    word ptr [si+1]        ;run the command routine
  101.     clc                ;then return Carry Flag = False
  102.     ret                ;as success flag
  103. intr6:    lodsb                ;look for end of this command string
  104.     or    al,al
  105.     jnz    intr6            ;not end yet, loop
  106.     add    si,2            ;skip over command routine offset
  107.     jmp    intr1            ;try to match next command
  108. intr7:    stc                ;no match on command, exit 
  109.     ret                ;with Carry = True
  110. intrinsic endp
  111.  
  112.  
  113. extrinsic proc    near            ;process an extrinsic command
  114.                     ;by passing it to COMMAND.COM
  115.                     ;with a " /C " command tail.
  116.     mov    al,cr            ;find length of the command
  117.     mov    cx,cmd_tail_length    ;by scanning for Carriage Return
  118.     mov    di,offset cmd_tail+1
  119.     cld
  120.     repnz scasb
  121.     mov    ax,di            ;calc length of command tail,
  122.     sub    ax,offset cmd_tail+2    ;not including the Carriage Return
  123.                     ;store length of synthesized 
  124.     mov    cmd_tail,al        ;command tail for EXEC function
  125.     mov    par_cmd,offset cmd_tail    ;address of command tail 
  126.     call    exec            ;call the EXEC function to pass
  127.     ret                ;command line to COMMAND.COM
  128. extrinsic endp
  129.  
  130.  
  131. get_cmd    proc    near            ;Prompt user and get a command.
  132.     mov    dx,offset prompt    ;display the shell prompt
  133.     mov    cx,prompt_length    ;on the Standard Output Device
  134.     mov    bx,stdout
  135.     mov    ah,40h            ;Function 40H=Write file or device
  136.     int    21h
  137.     mov    dx,offset inp_buf    ;get a line from the Standard
  138.     mov    cx,inp_buf_length    ;Input Device and place in our
  139.     mov    bx,stdin        ;command line buffer
  140.     mov    ah,3fh            ;Function 3FH=Read file or device
  141.     int    21h
  142.     mov    si,offset inp_buf    ;fold all lower case characters
  143.     mov    cx,inp_buf_length    ;in the command line to upper case
  144. gcmd1:    cmp    byte ptr [si],'a'
  145.     jb    gcmd2
  146.     cmp    byte ptr [si],'z'
  147.     ja    gcmd2
  148.     sub    byte ptr [si],'a'-'A'
  149. gcmd2:    inc    si
  150.     loop    gcmd1
  151.     ret                ;back to caller
  152. get_cmd    endp
  153.  
  154.  
  155. get_comspec proc near            ;Get file specification of COMMAND.COM
  156.                     ;from Environment "COMSPEC=" variable.
  157.                     ;Returns Carry=False if COMSPEC found.
  158.                     ;Returns Carry=True if no COMSPEC.
  159.     mov    si,offset com_var    ;let DS:SI = string to match...
  160.     call    get_env            ;go search Environment Block
  161.                     ;if environment variable not found,
  162.     jc    gcsp2            ;return Carry=True as failure code
  163.                     ;if var found, ES:DI points past "="
  164.     mov     si,offset com_spec    ;copy Env variable to our data segment 
  165. gcsp1:    mov    al,es:[di]        ;transfer null-terminated string
  166.     mov    [si],al
  167.     inc    si
  168.     inc    di
  169.     or    al,al            ;null found yet? (turns off Carry)
  170.     jnz    gcsp1            ;no, get next character
  171.                     ;success, return Carry Flag=False
  172. gcsp2:    ret
  173. get_comspec endp
  174.  
  175.  
  176. get_env    proc    near            ;Search Environment Block
  177.                     ;Call DS:SI = "NAME=" to match
  178.                     ;Uses contents of "ENV_SEG" 
  179.                     ;Returns Carry=False and ES:DI 
  180.                     ; pointing to parameter if found,
  181.                     ;Returns Carry=True if no match
  182.     mov    es,env_seg        ;get segment of Environment Block
  183.     xor    di,di            ;initialize offset to Env Block
  184. genv1:    mov    bx,si            ;initialize pointer to pattern
  185.     cmp    byte ptr es:[di],0    ;end of Environment block?
  186.     jne    genv2            ;jump if end not found
  187.     stc                ;return Carry = True as failure flag
  188.     ret
  189. genv2:    mov    al,[bx]            ;get character from pattern
  190.     or    al,al            ;end of pattern? (turns off Carry)
  191.     jz    genv3            ;yes, entire match succeeded
  192.     cmp    al,es:[di]        ;compare to char in Environment Block
  193.     jne    genv4            ;jump if match failed
  194.     inc    bx
  195.     inc    di
  196.     jmp    genv2
  197. genv3:                       ;All matched, return ES:DI pointing
  198.     ret                ;to parameter, Carry Flag = False
  199. genv4:    xor    al,al            ;scan forward in Environment Block
  200.     mov    cx,-1            ;for zero byte
  201.     cld
  202.     repnz    scasb
  203.     jmp    genv1            ;go compare next string
  204. get_env    endp
  205.  
  206.  
  207. exec    proc    near            ;call MS-DOS EXEC function
  208.                     ;to run COMMAND.COM. 
  209.     push    ds            ;save data segments
  210.     push    es
  211.     mov      cs:stk_seg,ss        ;save copy of SS:SP for use
  212.         mov     cs:stk_ptr,sp        ;after return from overlay
  213.     mov    dx,offset com_spec    ;now load and execute COMMAND.COM
  214.     mov    bx,offset par_blk
  215.     mov    ah,4bh            ;function 4BH = EXEC
  216.     mov    al,0            ;subfunction 0 = load and execute
  217.     int    21h
  218.     mov    ss,cs:stk_seg        ;restore stack segment
  219.     mov    sp,cs:stk_ptr        ;and stack pointer
  220.     pop    es            ;restore data segments
  221.     pop    ds
  222.     jnc    exec1            ;jump if no errors
  223.     mov    dx,offset msg2        ;EXEC failed, print error
  224.     mov    cx,msg2_length        ;message
  225.     mov    bx,stderr
  226.     mov    ah,40h
  227.     int    21h
  228. exec1:    ret                ;back to caller
  229. exec    endp
  230.  
  231.  
  232. cls_cmd    proc    near            ;intrinsic CLS Command 
  233.                     ;  = clear the screen
  234.     mov    dx,offset cls_str    ;send the ANSI control sequence
  235.     mov    cx,cls_str_length    ;to clear the screen
  236.     mov    bx,stdout
  237.     mov    ah,40h
  238.     int    21h
  239.     ret
  240. cls_cmd    endp
  241.  
  242.  
  243. dos_cmd    proc    near            ;intrinsic DOS Command 
  244.                     ;  = run COMMAND.COM
  245.     mov    par_cmd,offset nultail    ;set command tail to null string
  246.     call    exec            ;now EXEC the COMMAND.COM
  247.     ret
  248. dos_cmd    endp
  249.  
  250.  
  251. exit_cmd proc    near            ;intrinsic EXIT Command 
  252.                     ;  = leave this shell
  253.     mov    ax,4c00h        ;call DOS terminate function
  254.     int    21h            ;with return code of zero
  255. exit_cmd endp
  256.  
  257.  
  258. cseg    ends
  259.  
  260.  
  261. stack    segment    para stack 'STACK'    ;declare stack segment
  262.     dw    64 dup (?)
  263. stack    ends
  264.  
  265.  
  266. data    segment    para public 'DATA'    ;declare data segment
  267.  
  268. commands equ $                ;table of "intrinsic" commands
  269.                     ;each entry is a null-terminated
  270.                     ;string, followed by the offset
  271.                     ;of the procedure to be executed
  272.                     ;for that command.
  273.     db    'CLS',0
  274.     dw    cls_cmd
  275.     db    'DOS',0
  276.     dw    dos_cmd
  277.     db    'EXIT',0
  278.     dw    exit_cmd
  279.     db    0            ;table terminated with null string
  280.  
  281. com_var db    'COMSPEC=',0        ;Environment Block variable to match
  282.  
  283.                     ;filespec of COMMAND.COM moved
  284. com_spec db    80 dup (0)        ;here from the Environment Block
  285.  
  286. nultail    db    0,cr            ;a "Null" command tail for invoking
  287.                     ;COMMAND.COM as another shell
  288.  
  289. cmd_tail db    0,' /C '        ;command tail invoking COMMAND.COM
  290.                     ;as a transient command processor
  291.  
  292. inp_buf    db    80 dup (0)        ;command line from Standard Input
  293.  
  294. inp_buf_length equ $-inp_buf        
  295. cmd_tail_length equ $-cmd_tail-1
  296.  
  297. prompt    db    cr,lf,'sh: '        ;the shell's prompt to the user
  298. prompt_length equ $-prompt
  299.  
  300. env_seg    dw    0            ;segment of Environment Block
  301.  
  302. msg1    db    cr,lf
  303.     db    'Unable to de-allocate memory.'
  304.     db    cr,lf
  305. msg1_length equ $-msg1
  306.  
  307. msg2    db    cr,lf
  308.     db    'EXEC of COMMAND.COM failed.'
  309.     db    cr,lf
  310. msg2_length equ $-msg2
  311.  
  312. msg3    db    cr,lf
  313.     db    'No COMSPEC variable in Environment.'
  314.     db    cr,lf
  315. msg3_length equ $-msg3
  316.  
  317. cls_str    db    esc,'[2J'        ;this is the ANSI standard control
  318. cls_str_length equ $-cls_str        ;sequence to clear the screen
  319.  
  320. par_blk    equ    $            ;Parameter Block for EXEC call
  321.            dw    0            ;segment address, environment block
  322. par_cmd    dw    offset cmd_tail        ;address of command line
  323.     dw    seg cmd_tail
  324.     dd    -1            ;address of default FCB #1
  325.     dd    -1            ;address of default FCB #2
  326.  
  327. data    ends
  328.  
  329.     end    shell
  330.