home *** CD-ROM | disk | FTP | other *** search
- name shell
- page 55,132
- title 'SHELL.ASM -- simple MS-DOS shell'
- ;
- ; SHELL.ASM a simple user-extendable command interpreter
- ; for MS-DOS 2.X or 3.X
- ;
- ; Copyright (C) 1985 by Ray Duncan
- ;
- ; To assemble and link this program into the executable SHELL.EXE:
- ;
- ; C>MASM SHELL;
- ; C>LINK SHELL;
- ;
-
- stdin equ 0 ;Standard Input Device
- stdout equ 1 ;Standard Output Device
- stderr equ 2 ;Standard Error Device
-
- cr equ 0dh ;ASCII carriage return
- lf equ 0ah ;ASCII line feed
- blank equ 20h ;ASCII blank code
- esc equ 01bh ;ASCII escape code
-
- cseg segment para public 'CODE'
-
- assume cs:cseg,ds:data,ss:stack
-
- shell proc far ;at entry DS = ES = PSP
- mov ax,data ;let DS point to our data segment
- mov ds,ax
- mov ax,es:[002ch] ;get segment of Environment Block
- mov env_seg,ax ;from PSP and save it.
- ;now release unneeded memory...
- mov bx,100h ;ES=segment PSP, BX=paragraphs needed
- mov ah,4ah ;Function 4AH = modify memory block
- int 21h ;transfer to DOS
- jnc shell1 ;jump if request successful
- mov dx,offset msg1 ;otherwise display error message
- mov cx,msg1_length ;and exit
- jmp shell4
- shell1: call get_comspec ;get file spec for COMMAND.COM
- jnc shell2 ;jump if it was found ok
- mov dx,offset msg3 ;COMSPEC variable not found in
- mov cx,msg3_length ;Environment block, print error
- jmp shell4 ;message and exit
- shell2: mov dx,offset shell3 ;set Control-C vector (Int 23H)
- mov ax,cs ;so that shell can keep control
- mov ds,ax ;DS:DX = addr of Control-C handler
- mov ax,2523H ;Function 25H = Set Interrupt
- int 21h
- mov ax,data ;make DS and ES point to our
- mov ds,ax ;data segment again
- mov es,ax
- shell3: ;main loop of command interpreter
- call get_cmd ;get a command from user
- call intrinsic ;check if intrinsic function
- jnc shell3 ;yes, it was processed
- call extrinsic ;no, pass it to COMMAND.COM
- jmp shell3 ;then get another command
- shell4: ;come here if error detected, with
- ;DS:DX=message addr, CX=length
- mov bx,stderr ;print error message on
- mov ah,40h ;Standard Error Device
- int 21h
- mov ax,4c01h ;exit to DOS with return code = 1
- int 21h ;indicating an error condition
- shell endp
-
- ;these variables must be in Code Seg
- stk_seg dw 0 ;original SS contents
- stk_ptr dw 0 ;original SP contents
-
-
- intrinsic proc near ;Decode a user command against
- ;the table "COMMANDS". If match
- ;found, run the subroutine, and
- ;return Carry = False. If no match,
- ;return Carry = True.
- mov si,offset commands ;let SI = start of command table
- intr1: cmp byte ptr [si],0 ;is command table exhausted?
- je intr7 ;jump, end of table found
- mov di,offset inp_buf ;no, let DI = addr of user input
- intr2: cmp byte ptr [di],blank ;scan off any leading blanks
- jne intr3
- inc di
- jmp intr2
- intr3: mov al,[si] ;get next command char
- or al,al ;check if end of string
- jz intr4 ;jump, entire string matched
- cmp al,[di] ;compare to next input char
- jnz intr6 ;jump, found mismatch
- inc si ;go to next char in strings
- inc di
- jmp intr3
- intr4: cmp byte ptr [di],cr ;make sure user's command
- je intr5 ;is the same length, next char
- cmp byte ptr [di],blank ;must be blank or Return
- jne intr6
- intr5: call word ptr [si+1] ;run the command routine
- clc ;then return Carry Flag = False
- ret ;as success flag
- intr6: lodsb ;look for end of this command string
- or al,al
- jnz intr6 ;not end yet, loop
- add si,2 ;skip over command routine offset
- jmp intr1 ;try to match next command
- intr7: stc ;no match on command, exit
- ret ;with Carry = True
- intrinsic endp
-
-
- extrinsic proc near ;process an extrinsic command
- ;by passing it to COMMAND.COM
- ;with a " /C " command tail.
- mov al,cr ;find length of the command
- mov cx,cmd_tail_length ;by scanning for Carriage Return
- mov di,offset cmd_tail+1
- cld
- repnz scasb
- mov ax,di ;calc length of command tail,
- sub ax,offset cmd_tail+2 ;not including the Carriage Return
- ;store length of synthesized
- mov cmd_tail,al ;command tail for EXEC function
- mov par_cmd,offset cmd_tail ;address of command tail
- call exec ;call the EXEC function to pass
- ret ;command line to COMMAND.COM
- extrinsic endp
-
-
- get_cmd proc near ;Prompt user and get a command.
- mov dx,offset prompt ;display the shell prompt
- mov cx,prompt_length ;on the Standard Output Device
- mov bx,stdout
- mov ah,40h ;Function 40H=Write file or device
- int 21h
- mov dx,offset inp_buf ;get a line from the Standard
- mov cx,inp_buf_length ;Input Device and place in our
- mov bx,stdin ;command line buffer
- mov ah,3fh ;Function 3FH=Read file or device
- int 21h
- mov si,offset inp_buf ;fold all lower case characters
- mov cx,inp_buf_length ;in the command line to upper case
- gcmd1: cmp byte ptr [si],'a'
- jb gcmd2
- cmp byte ptr [si],'z'
- ja gcmd2
- sub byte ptr [si],'a'-'A'
- gcmd2: inc si
- loop gcmd1
- ret ;back to caller
- get_cmd endp
-
-
- get_comspec proc near ;Get file specification of COMMAND.COM
- ;from Environment "COMSPEC=" variable.
- ;Returns Carry=False if COMSPEC found.
- ;Returns Carry=True if no COMSPEC.
- mov si,offset com_var ;let DS:SI = string to match...
- call get_env ;go search Environment Block
- ;if environment variable not found,
- jc gcsp2 ;return Carry=True as failure code
- ;if var found, ES:DI points past "="
- mov si,offset com_spec ;copy Env variable to our data segment
- gcsp1: mov al,es:[di] ;transfer null-terminated string
- mov [si],al
- inc si
- inc di
- or al,al ;null found yet? (turns off Carry)
- jnz gcsp1 ;no, get next character
- ;success, return Carry Flag=False
- gcsp2: ret
- get_comspec endp
-
-
- get_env proc near ;Search Environment Block
- ;Call DS:SI = "NAME=" to match
- ;Uses contents of "ENV_SEG"
- ;Returns Carry=False and ES:DI
- ; pointing to parameter if found,
- ;Returns Carry=True if no match
- mov es,env_seg ;get segment of Environment Block
- xor di,di ;initialize offset to Env Block
- genv1: mov bx,si ;initialize pointer to pattern
- cmp byte ptr es:[di],0 ;end of Environment block?
- jne genv2 ;jump if end not found
- stc ;return Carry = True as failure flag
- ret
- genv2: mov al,[bx] ;get character from pattern
- or al,al ;end of pattern? (turns off Carry)
- jz genv3 ;yes, entire match succeeded
- cmp al,es:[di] ;compare to char in Environment Block
- jne genv4 ;jump if match failed
- inc bx
- inc di
- jmp genv2
- genv3: ;All matched, return ES:DI pointing
- ret ;to parameter, Carry Flag = False
- genv4: xor al,al ;scan forward in Environment Block
- mov cx,-1 ;for zero byte
- cld
- repnz scasb
- jmp genv1 ;go compare next string
- get_env endp
-
-
- exec proc near ;call MS-DOS EXEC function
- ;to run COMMAND.COM.
- push ds ;save data segments
- push es
- mov cs:stk_seg,ss ;save copy of SS:SP for use
- mov cs:stk_ptr,sp ;after return from overlay
- mov dx,offset com_spec ;now load and execute COMMAND.COM
- mov bx,offset par_blk
- mov ah,4bh ;function 4BH = EXEC
- mov al,0 ;subfunction 0 = load and execute
- int 21h
- mov ss,cs:stk_seg ;restore stack segment
- mov sp,cs:stk_ptr ;and stack pointer
- pop es ;restore data segments
- pop ds
- jnc exec1 ;jump if no errors
- mov dx,offset msg2 ;EXEC failed, print error
- mov cx,msg2_length ;message
- mov bx,stderr
- mov ah,40h
- int 21h
- exec1: ret ;back to caller
- exec endp
-
-
- cls_cmd proc near ;intrinsic CLS Command
- ; = clear the screen
- mov dx,offset cls_str ;send the ANSI control sequence
- mov cx,cls_str_length ;to clear the screen
- mov bx,stdout
- mov ah,40h
- int 21h
- ret
- cls_cmd endp
-
-
- dos_cmd proc near ;intrinsic DOS Command
- ; = run COMMAND.COM
- mov par_cmd,offset nultail ;set command tail to null string
- call exec ;now EXEC the COMMAND.COM
- ret
- dos_cmd endp
-
-
- exit_cmd proc near ;intrinsic EXIT Command
- ; = leave this shell
- mov ax,4c00h ;call DOS terminate function
- int 21h ;with return code of zero
- exit_cmd endp
-
-
- cseg ends
-
-
- stack segment para stack 'STACK' ;declare stack segment
- dw 64 dup (?)
- stack ends
-
-
- data segment para public 'DATA' ;declare data segment
-
- commands equ $ ;table of "intrinsic" commands
- ;each entry is a null-terminated
- ;string, followed by the offset
- ;of the procedure to be executed
- ;for that command.
- db 'CLS',0
- dw cls_cmd
- db 'DOS',0
- dw dos_cmd
- db 'EXIT',0
- dw exit_cmd
- db 0 ;table terminated with null string
-
- com_var db 'COMSPEC=',0 ;Environment Block variable to match
-
- ;filespec of COMMAND.COM moved
- com_spec db 80 dup (0) ;here from the Environment Block
-
- nultail db 0,cr ;a "Null" command tail for invoking
- ;COMMAND.COM as another shell
-
- cmd_tail db 0,' /C ' ;command tail invoking COMMAND.COM
- ;as a transient command processor
-
- inp_buf db 80 dup (0) ;command line from Standard Input
-
- inp_buf_length equ $-inp_buf
- cmd_tail_length equ $-cmd_tail-1
-
- prompt db cr,lf,'sh: ' ;the shell's prompt to the user
- prompt_length equ $-prompt
-
- env_seg dw 0 ;segment of Environment Block
-
- msg1 db cr,lf
- db 'Unable to de-allocate memory.'
- db cr,lf
- msg1_length equ $-msg1
-
- msg2 db cr,lf
- db 'EXEC of COMMAND.COM failed.'
- db cr,lf
- msg2_length equ $-msg2
-
- msg3 db cr,lf
- db 'No COMSPEC variable in Environment.'
- db cr,lf
- msg3_length equ $-msg3
-
- cls_str db esc,'[2J' ;this is the ANSI standard control
- cls_str_length equ $-cls_str ;sequence to clear the screen
-
- par_blk equ $ ;Parameter Block for EXEC call
- dw 0 ;segment address, environment block
- par_cmd dw offset cmd_tail ;address of command line
- dw seg cmd_tail
- dd -1 ;address of default FCB #1
- dd -1 ;address of default FCB #2
-
- data ends
-
- end shell