home *** CD-ROM | disk | FTP | other *** search
/ Microsoft Programmer's Library 1.3 / Microsoft-Programers-Library-v1.3.iso / sampcode / advmsdos / chap12 / shell.asm next >
Encoding:
Assembly Source File  |  1988-10-01  |  10.0 KB  |  418 lines

  1.     name      shell    
  2.     page      55,132
  3.     title     SHELL.ASM -- simple MS-DOS shell
  4. ; SHELL.ASM    Simple extendable command interpreter
  5. ;         for MS-DOS version 2.0 and later
  6. ; Copyright 1988 by Ray Duncan
  7. ; Build:    C>MASM SHELL; 
  8. ;         C>LINK SHELL; 
  9. ; Usage:    C>SHELL;
  10. ;
  11.  
  12. stdin    equ    0            ; standard input handle
  13. stdout    equ    1            ; standard output handle
  14. stderr    equ    2            ; standard error handle
  15.  
  16. cr    equ    0dh             ; ASCII carriage return
  17. lf      equ     0ah               ; ASCII line feed
  18. blank    equ    20h            ; ASCII blank code
  19. escape    equ    01bh            ; ASCII escape code
  20.  
  21. _TEXT     segment    word public 'CODE'
  22.  
  23.     assume     cs:_TEXT,ds:_DATA,ss:STACK
  24.  
  25. shell    proc    far            ; at entry DS = ES = PSP
  26.  
  27.     mov    ax,_DATA        ; make our data segment
  28.     mov    ds,ax            ; addressable
  29.  
  30.     mov    ax,es:[002ch]        ; get environment segment
  31.     mov    env_seg,ax        ; from PSP and save it
  32.  
  33.                     ; release unneeded memory...
  34.                     ; ES already = PSP segment
  35.     mov      bx,100h           ; BX = paragraphs needed
  36.         mov     ah,4ah            ; Function 4AH = resize block
  37.         int     21h            ; transfer to MS-DOS
  38.     jnc    shell1            ; jump if resize OK
  39.  
  40.     mov    dx,offset msg1        ; resize failed, display
  41.     mov    cx,msg1_length        ; error message and exit
  42.     jmp    shell4
  43.  
  44. shell1:    call    get_comspec        ; get COMMAND.COM filespec
  45.     jnc    shell2            ; jump if it was found 
  46.  
  47.     mov    dx,offset msg3        ; COMSPEC not found in 
  48.     mov    cx,msg3_length        ; environment, display error
  49.     jmp    shell4            ; message and exit
  50.  
  51. shell2:    mov    dx,offset shell3    ; set Ctrl-C vector (Int 23H)
  52.     mov    ax,cs            ; for this program's handler
  53.     mov    ds,ax            ; DS:DX = handler address
  54.     mov    ax,2523H        ; Fxn 25H = set vector
  55.     int    21h            ; transfer to MS-DOS
  56.  
  57.     mov    ax,_DATA        ; make our data segment
  58.     mov    ds,ax            ; addressable again
  59.     mov    es,ax
  60.  
  61. shell3:                    ; main interpreter loop
  62.  
  63.     call    get_cmd            ; get a command from user
  64.  
  65.     call    intrinsic        ; check if intrinsic function
  66.     jnc    shell3            ; yes, it was processed
  67.  
  68.     call    extrinsic        ; no, pass it to COMMAND.COM 
  69.     jmp    shell3            ; then get another command
  70.  
  71. shell4:                 ; come here if error detected
  72.                     ; DS:DX = message address
  73.                     ; CX = message length
  74.     mov    bx,stderr        ; BX = standard error handle
  75.     mov    ah,40h            ; Fxn 40H = write
  76.     int    21h            ; transfer to MS-DOS
  77.  
  78.     mov    ax,4c01h        ; Fxn 4CH = terminate with
  79.                     ; return code = 1
  80.     int    21h            ; transfer to MS-DOS
  81.  
  82. shell    endp
  83.  
  84.  
  85. intrinsic proc    near            ; Decode user entry against
  86.                     ; the table "COMMANDS" 
  87.                     ; If match, run the routine,
  88.                     ; and return Carry = False 
  89.                     ; If no match, Carry = True
  90.                     ; return Carry = True.
  91.  
  92.     mov    si,offset commands    ; DS:SI = command table
  93.  
  94. intr1:    cmp    byte ptr [si],0        ; end of table?
  95.     je    intr7            ; jump, end of table found
  96.  
  97.     mov    di,offset inp_buf    ; no, let DI = addr of user input
  98.  
  99. intr2:    cmp    byte ptr [di],blank    ; scan off any leading blanks 
  100.     jne    intr3
  101.  
  102.     inc    di            ; found blank, go past it
  103.     jmp    intr2
  104.  
  105. intr3:    mov    al,[si]            ; next character from table
  106.  
  107.     or    al,al            ; end of string?
  108.     jz    intr4            ; jump, entire string matched
  109.  
  110.     cmp    al,[di]            ; compare to input character
  111.     jnz    intr6            ; jump, found mismatch
  112.  
  113.     inc    si            ; advance string pointers
  114.     inc    di
  115.     jmp    intr3
  116.  
  117. intr4:    cmp    byte ptr [di],cr    ; make sure user's entry
  118.     je    intr5            ; is the same length...
  119.     cmp    byte ptr [di],blank    ; next character in entry
  120.     jne    intr6            ; must be blank or Return
  121.  
  122. intr5:    call    word ptr [si+1]        ; run the command routine
  123.  
  124.     clc                ; return Carry Flag = False
  125.     ret                ; as success flag
  126.  
  127. intr6:    lodsb                ; look for end of this
  128.     or    al,al            ; command string (null byte)
  129.     jnz    intr6            ; not end yet, loop
  130.  
  131.     add    si,2            ; skip over routine address
  132.     jmp    intr1            ; try to match next command
  133.  
  134. intr7:    stc                ; command not matched, exit
  135.     ret                ; with Carry = True
  136.  
  137. intrinsic endp
  138.  
  139.  
  140. extrinsic proc    near            ; process extrinsic command
  141.                     ; by passing it to 
  142.                     ; COMMAND.COM with a
  143.                     ; " /C " command tail.
  144.  
  145.     mov    al,cr            ; find length of command
  146.     mov    cx,cmd_tail_length    ; by scanning for carriage
  147.     mov    di,offset cmd_tail+1    ; return
  148.     cld
  149.     repnz scasb
  150.  
  151.     mov    ax,di            ; calculate command tail
  152.     sub    ax,offset cmd_tail+2    ; length without carriage
  153.     mov    cmd_tail,al        ; return, and store it
  154.  
  155.                     ; set command tail address
  156.     mov    word ptr par_cmd,offset cmd_tail
  157.     call    exec            ; and run COMMAND.COM
  158.     ret
  159.  
  160. extrinsic endp
  161.  
  162.  
  163. get_cmd    proc    near            ; prompt user, get command
  164.  
  165.                     ; display the shell prompt
  166.     mov    dx,offset prompt    ; DS:DX = message address
  167.     mov    cx,prompt_length    ; CX = message length
  168.     mov    bx,stdout        ; BX = standard output handle
  169.     mov    ah,40h            ; Fxn 40H = write
  170.     int    21h            ; transfer to MS-DOS
  171.  
  172.                     ; get entry from user
  173.     mov    dx,offset inp_buf    ; DS:DX = input buffer
  174.     mov    cx,inp_buf_length    ; CX = max length to read
  175.     mov    bx,stdin        ; BX = standard input handle
  176.     mov    ah,3fh            ; Fxn 3FH = read
  177.     int    21h            ; transfer to MS-DOS
  178.  
  179.     mov    si,offset inp_buf    ; fold lower case characters
  180.     mov    cx,inp_buf_length    ; in entry to upper case
  181.  
  182. gcmd1:    cmp    byte ptr [si],'a'    ; check if 'a-z'
  183.     jb    gcmd2            ; jump, not in range
  184.     cmp    byte ptr [si],'z'    ; check if 'a-z'    
  185.     ja    gcmd2            ; jump, not in range
  186.     sub    byte ptr [si],'a'-'A'    ; convert to upper case
  187.  
  188. gcmd2:    inc    si            ; advance through entry
  189.     loop    gcmd1
  190.     ret                ; back to caller
  191.  
  192. get_cmd    endp
  193.  
  194.  
  195. get_comspec proc near            ; get location of COMMAND.COM
  196.                     ; from environment "COMSPEC=" 
  197.                     ; Returns Carry = False 
  198.                     ; if COMSPEC found.
  199.                     ; Returns Carry=True 
  200.                     ; if no COMSPEC.
  201.  
  202.     mov    si,offset com_var    ; DS:SI = string to match...
  203.     call    get_env            ; search Environment Block
  204.     jc    gcsp2            ; jump if COMSPEC not found
  205.  
  206.                     ; ES:DI points past "="
  207.     mov     si,offset com_spec    ; DS:SI = local buffer
  208.  
  209. gcsp1:    mov    al,es:[di]        ; copy COMSPEC variable
  210.     mov    [si],al            ; to local buffer
  211.     inc    si
  212.     inc    di
  213.     or    al,al            ; null char? (turns off Carry)
  214.     jnz    gcsp1            ; no, get next character
  215.  
  216. gcsp2:    ret                ; back to caller
  217.  
  218. get_comspec endp
  219.  
  220.  
  221. get_env    proc    near            ; search environment
  222.                     ; Call DS:SI = "NAME=" 
  223.                     ; uses contents of "ENV_SEG" 
  224.                     ; Returns Carry=False and ES:DI 
  225.                     ; pointing to parameter if found,
  226.                     ; Returns Carry=True if no match
  227.  
  228.     mov    es,env_seg        ; get environment segment
  229.     xor    di,di            ; initialize env. offset 
  230.  
  231. genv1:    mov    bx,si            ; initialize pointer to name
  232.     cmp    byte ptr es:[di],0    ; end of environment?
  233.     jne    genv2            ; jump, end not found
  234.  
  235.     stc                ; no match, return Carry set
  236.     ret
  237.  
  238. genv2:    mov    al,[bx]            ; get character from name
  239.     or    al,al            ; end of name? (turns off Carry)
  240.     jz    genv3            ; yes, name matched
  241.  
  242.     cmp    al,es:[di]        ; compare to environment
  243.     jne    genv4            ; jump if match failed
  244.  
  245.     inc    bx            ; advance environment
  246.     inc    di            ; and name pointers
  247.     jmp    genv2
  248.  
  249. genv3:                    ; match found, Carry = clear,
  250.     ret                 ; ES:DI = variable
  251.  
  252. genv4:    xor    al,al            ; scan forward in environment
  253.     mov    cx,-1            ; for zero byte
  254.     cld
  255.     repnz    scasb
  256.     jmp    genv1            ; go compare next string
  257.  
  258. get_env    endp
  259.  
  260.  
  261. exec    proc    near            ; call MS-DOS EXEC function
  262.                     ; to run COMMAND.COM. 
  263.  
  264.     mov      stkseg,ss        ; save stack pointer
  265.         mov     stkptr,sp
  266.  
  267.                     ; now run COMMAND.COM
  268.     mov    dx,offset com_spec    ; DS:DX = filename
  269.     mov    bx,offset par_blk    ; ES:BX = parameter block
  270.     mov    ax,4b00h        ; Fxn 4BH = EXEC
  271.                     ; Subf. 0 = load and execute
  272.     int    21h            ; transfer to MS-DOS
  273.  
  274.     mov    ax,_DATA        ; make data segment
  275.     mov    ds,ax            ; addressable again
  276.     mov    es,ax
  277.  
  278.     cli                 ; (for bug in some 8088s)
  279.     mov    ss,stkseg        ; restore stack pointer
  280.     mov    sp,stkptr
  281.     sti                ; (for bug in some 8088s)
  282.  
  283.     jnc    exec1            ; jump if no errors
  284.  
  285.                     ; display error message
  286.     mov    dx,offset msg2        ; DS:DX = message address
  287.     mov    cx,msg2_length        ; CX = message length
  288.     mov    bx,stderr        ; BX = standard error handle
  289.     mov    ah,40h            ; Fxn 40H = write
  290.     int    21h            ; transfer to MS-DOS
  291.  
  292. exec1:    ret                ; back to caller
  293.  
  294. exec    endp
  295.  
  296.  
  297. cls_cmd    proc    near            ; intrinsic CLS command 
  298.  
  299.     mov    dx,offset cls_str    ; send the ANSI escape
  300.     mov    cx,cls_str_length    ; sequence to clear
  301.     mov    bx,stdout        ; the screen    
  302.     mov    ah,40h
  303.     int    21h
  304.     ret
  305.  
  306. cls_cmd    endp
  307.  
  308.  
  309. dos_cmd    proc    near            ; intrinsic DOS command 
  310.  
  311.                     ; set null command tail
  312.     mov    word ptr par_cmd,offset nultail
  313.     call    exec            ; and run COMMAND.COM
  314.     ret
  315.  
  316. dos_cmd    endp
  317.  
  318.  
  319. exit_cmd proc    near            ; intrinsic EXIT Command 
  320.  
  321.     mov    ax,4c00h        ; call MS-DOS terminate
  322.     int    21h            ; function with 
  323.                     ; return code of zero
  324. exit_cmd endp
  325.  
  326. _TEXT    ends
  327.  
  328.  
  329. STACK    segment    para stack 'STACK'    ; declare stack segment
  330.  
  331.     dw    64 dup (?)
  332.  
  333. STACK    ends
  334.  
  335.  
  336. _DATA    segment    word public 'DATA'
  337.  
  338. commands equ $                ; "intrinsic" commands table
  339.                     ; each entry is ASCIIZ string
  340.                     ; followed by the offset
  341.                     ; of the procedure to be 
  342.                     ; executed for that command.
  343.     db    'CLS',0
  344.     dw    cls_cmd
  345.  
  346.     db    'DOS',0
  347.     dw    dos_cmd
  348.  
  349.     db    'EXIT',0
  350.     dw    exit_cmd
  351.  
  352.     db    0            ; end of table
  353.  
  354. com_var db    'COMSPEC=',0        ; environment variable
  355.  
  356.                     ; COMMAND.COM filespec 
  357. com_spec db    80 dup (0)        ; from environment COMSPEC=
  358.  
  359. nultail    db    0,cr            ; null command tail for
  360.                     ; invoking COMMAND.COM
  361.                     ; as another shell
  362.  
  363. cmd_tail db    0,' /C '        ; command tail for invoking 
  364.                     ; COMMAND.COM  as a transient
  365.  
  366. inp_buf    db    80 dup (0)        ; command line from Standard Input
  367.  
  368. inp_buf_length equ $-inp_buf        
  369. cmd_tail_length equ $-cmd_tail-1
  370.  
  371. prompt    db    cr,lf,'sh: '        ; SHELL's user prompt
  372. prompt_length equ $-prompt
  373.  
  374. env_seg    dw    0            ; segment of Environment Block
  375.  
  376. msg1    db    cr,lf
  377.     db    'Unable to release memory.'
  378.     db    cr,lf
  379. msg1_length equ $-msg1
  380.  
  381. msg2    db    cr,lf
  382.     db    'EXEC of COMMAND.COM failed.'
  383.     db    cr,lf
  384. msg2_length equ $-msg2
  385.  
  386. msg3    db    cr,lf
  387.     db    'No COMSPEC variable in environment.'
  388.     db    cr,lf
  389. msg3_length equ $-msg3
  390.  
  391. cls_str    db    escape,'[2J'        ; ANSI escape sequence
  392. cls_str_length equ $-cls_str        ; to clear the screen
  393.  
  394.                     ; EXEC parameter block
  395. par_blk    dw    0            ; environment segment
  396. par_cmd    dd    cmd_tail        ; command line
  397.     dd    fcb1            ; file control block #1
  398.     dd    fcb2            ; file control block #2
  399.  
  400. fcb1    db    0            ; file control block #1
  401.     db    11 dup (' ')
  402.     db    25 dup (0)
  403.  
  404. fcb2    db    0            ; file control block #2
  405.     db    11 dup (' ')
  406.     db    25 dup (0)
  407.  
  408. stkseg    dw    0                 ; original SS contents
  409. stkptr     dw      0                 ; original SP contents
  410.  
  411. _DATA    ends
  412.  
  413.     end    shell
  414.