;************************* PUSHD.ASM ************************************* ; ; PUSHD.ASM version 1.2 ; ; Usage: ; PUSHD directory ; Will push the current directory onto the directory stack ; if there is a location available and changes to the current ; directory to the specified parameter. ; PUSHD ; Will change the current directory to the directory stored ; in the top of the stack and will place the current directory ; on the top of the stack. ; PUSHD +n ; Will change the current directory to the directory stored ; in stack location n, with 1 being the bottom of the stack ; and 6 being the top. The stack will be compressed to remove ; any empty space and the current directory will be stored ; on top of the stack. ; Purpose: This program will store directories in a memory-resident ; area and change to a specified directory, so that the ; user may switch back to a previous directory without having ; to either know or to type in the required directory. If the ; program terminates abnormally an error message will be ; printed to the standard error. ; Preconditions: ; (The first seven conditions are required for functionality ; with POPD and DIRS.) ; 1) PUSHD must be hooked to interrupt 13. ; 2) PUSHD must have the same commands from beginning ; to after definition of push6d, so that it knows how ; to access the resident code of PUSHD. (This includes ; the fact that there are 6 locations for directory storage ; which are 67 bytes in size.) ; 3) PUSHD must have the signature "PUSHD VERSION 1.1". ; 4) PUSHD must look for 7788 in ax and 7789 in bx for ; a call to int 13. ; 5) PUSHD must put the value of 7789 into ax and 7788 ; into bx upon calling int 13 by DIRS, if PUSHD is installed. ; 6) PUSHD will set the data segment to be equal to the data ; segment that PUSHD used to install itself. ; 7) DS:[nextpush] (DS of PUSHD installation) must hold ; the location of the next empty directory stack location. ; 8) For the last two usages PUSHD must already be installed, ; otherwise an error condition is generated. ; 9) Enough memory space must be available for the directory ; stack and other resident code (new interrupt, etc.) ; Postconditions: ; 1) An appropriate error message will be printed OR ; 2) The parameter directory will become the current directory ; and the current directory will be saved on the stack (usage ; #1), with DS:[nextpush] being incremented to the next ; storage position OR ; 3) The directory on the top of the stack will become the ; current directory and the current directory will be placed ; in the top position on the stack (usage #2) OR ; 4) The directory stored in location "n" (starting with 1 as ; the bottom and six as the top) will become the current ; directory with the contents of the stack storage above the ; the targeted directory shifting downward one to effectively ; eliminate the "gap" left by the targeted directory. Also, ; the current directory will be placed on top of the directory ; stack (usage #3) ; 5) If PUSHD was not previously loaded into memory, due to ; not use or abnormal termination, the program will now be ; loaded with a reduction of memory of 1024 bytes. This ; includes the storage space required for the stack and ; the new interrupt 13 handler. ; 6) An error code will be generated. ; Error Codes: ; 0) Successful completion. ; 249) Error popping directory off stack. ; 250) Error pushing current directory. ; 251) Directory Stack is full. ; 252) Error changing to directory given on command line. ; 253) Command Line parameter exceeds Directory Stack depth. ; 254) Illegal Characters on the Command Line. ; 255) Command Line parameter not in valid directory range (1-6). ; Version changes: ; PUSHD v. 1.2: July 21, 1990. ; coded in Toad Hall tweaks and suggestions (much ; thanks to David Kirschbaum). ; PUSHD v. 1.1: July 14, 1990. ; added "+n" option. ; added change to parameter option. ; changed no parameter option to switch current and ; top directories. ; changed hooked interrupt to 13 from 16. ; added error codes for abnormal termination. ; changed writes from vector 9 to vector 40h of ; int 21h. ; changed program termination from int 20h to vector ; 4Cxxh int 21h, where xx represents the error code ; generated. ; removed stack wrap around. ; PUSHDIR v. 1.0: Copyright 1986, Ziff-Davis Publishing Co. ; From PC Magazine, May 27, 1986 ; No changes. ; Future Enhancements: ; 1) Change the "n" parameters so that it refers to the ; top of the stack, not the bottom (RELATIVE from the top ; NOT ABSOLUTE from the bottom). ; 2) Make program an .EXE file. ; 3) Remove extra push/pop commands. ; 4) Fix up various hacks. ; 5) Correct error caused by 3 or more blanks at end of ; command line tail. ; 6) Add a help option, ?, from the command line. ; 7) have "signature" loaded into memory so that programs such ; as "POPDROP" can identify the stack as resident. ; ; Created by: ; (from ideas based in UNIX and from PC Magazine's POPDIR.COM) ; William P. Sarra ; CIS:71041,347 ; arpa: sarra@TOPAZ.RUTGERS.EDU ; uucp: ...{ames,cbosgd, harvard, moss}!rutgers!topaz.rutgers.edu!sarra ; ;***************************************************************************** MAIN group CSEG CSEG segment public para 'code' assume CS:MAIN,DS:MAIN,ES:MAIN,SS:MAIN org 2CH ;v1.2 envseg label word ;Environment Segment in PSP v1.2 org 80h cmd_line label byte org 100h ;.COM file Begin: jmp Start ;program starts here signature db 'PUSHD VERSION 1.1' SIG_LEN equ $-signature savedint13 dd ? ;old int 13h vector nextpush dw offset MAIN:push1d ;next place to save a dir push1d db 67 dup (0) ;storage for a saved dir push2d db 67 dup (0) ;more storage push3d db 67 dup (0) ;more storage push4d db 67 dup (0) ;more storage push5d db 67 dup (0) ;more storage push6d db 67 dup (0) ;last storage ;up to here must be EXACTLY identical in PUSHD, POPD, and DIRS so that ;POPD and DIRS can know how to access the memory space reserved by the first ;PUSHD. ;************************************************************************** ; ; PROCEDURE: MYINT13 ; ; Purpose: myint13 is an interrupt handler chained onto the existing ; interrupt handler. It is used to find out if PUSHD is ; already installed and if it is, where it located. It works ; by adding another function to int 13h. To use it ax = 7788h, ; bx = 7789h, and DS:si points to the signature string. If any ; one of these conditions are not true, then the int 13h call ; is passed onto the old routine without doing anything. If ; they are all true, then we switch ax and bx and return DS = ; code segment (CS) of the interrupt handler. ; Preconditions: ; For a check to see if PUSHD is installed, ax = 7788h and bx ; = 7789h DS:si points to the string "PUSHD VERSION 1.1". ; Postconditons: ; If PUSHD is installed, ax = 7789h and bx = 7788h, DS = CS of ; the interrupt handler. ; If PUSHD was not installed, a new function is added to ; interrupt 13h which is checked before any other functions. ; ;**************************************************************************** myint13 proc far pushf ;save flags cmp ax,7788h ;possible signature request ? je CHECKSIG ;yes NOTSIG: popf ;no - recover flags jmp CS:[savedint13] ;go to old routine as normal CHECKSIG: cmp bx,7789h ;possible signature request ? jne NOTSIG ;no ;ax and bx were correct for a signature request. ;Now see if DS:si was pointing to the signature string. ;Signature string MUST be unique from any other programs. push ES ;save the registers we will use push di push cx mov di,offset main:signature ;address of the signature mov cx,sig_len ;length of the signature repe cmpsb ;does string at DS:si match ES:di ? pop cx ;recover all registers we used pop di pop ES jne NOTSIG ;no, not correct signature ;Signature request: return DS equal to the current code segment so ; subsequent PUSHD's, POPD's and DIRS know where the original ; is located. push CS pop DS ;set DS = CS xchg ax,bx ;flip these two so we know that ;DS is being returned popf ;recover original flags iret ;return back to the program ;that called the int 13h myint13 endp ENDRESIDENT label byte ;ENDRESIDENT EQU (OFFSET CSEG - $) + 0FH ;v1.2 ;code after here will not remain resident ;********************************************************************** ; ; PROCEDURE: Push_Dir ; ; Purpose: This procedure will put the current directory into ; the storage location specified si. It will store ; the whole path to the current directory and will add ; the drive and root specification as well. ; Preconditions: ; DS = segment of the storage pointed to by si. ; si points to the storage location. ; The storage location is 67 bytes wide. ; The error message "abortmsg" is defined and it's length ; has been determined. ; Postconditions: ; si will be three more than the si passed into the ; procedure. ; si - 3 will point to beginning of the current directory, ; starting with the drive specification. ; OR an error message will be generated on stderr and the ; program will terminate with an error code. ; ;************************************************************************** Push_Dir proc ;DS = segment of the directory storage in si ASSUME DS:CSEG ;assume TSR v1.2 ;store the current directory, including disk drive letter add si,3 xor dl,dl ;default drive v1.2 mov ah,47h ;dos function number int 21h ;get current directory sub si,3 jc ABORTERR ;error message if carry set mov ah,19h ;dos function number int 21h ;get the current drive add al,'A' ;convert to ascii mov [si],al ;add the "D:\" in front of path v1.2 mov word ptr [SI+1],'\:' ;type them in backwards.. v1.2 ret ABORTERR: ;error in getting current directory -- generate error message ; and exit with an error code mov dx,offset MAIN:abortmsg ;address of error message mov cx,ABORTMSG_LN mov al,0FAH ;ERRORLEVEL v1.2 jmp Msg_Term ;display, terminate v1.2 Push_Dir endp ;******************************************************************** ; ; PROCEDURE: Pop_Dir ; ; Purpose: This procedure will change the current directory to ; the one specified in the location pointed to by ; bp. It will also change the current drive specification, ; if necessary. ; Preconditions: ; bp points to the location of the directory to change to. ; ES is equal to the segment that holds the targeted ; directory. ; The error message "errpop1" is defined and it's length ; has been determined. ; Postconditions: ; The current drive and directory are those specified in ; in the parameter pointed to by bp, if valid. ; Otherwise, an error message is printed and a specific ; error code is generated. ; ;************************************************************************** Pop_Dir proc ;bp holds location of directory to pop off stack ;set the current directory mov dx,bp ;load DS:dx with directory to set mov ah,3bh ;dos function number int 21h ;set current dir back jc ERRPOP ;branch on error mov dl,DS:[bp] ;v1.2 sub dl,'A' ;convert to binary (0=A, 1=B) mov ah,0eh ;dos function number int 21h ;set drive jc ERRPOP ret ERRPOP: ;Error in changing to the desired directory -- print an ; error message and exit with error code. mov dx,offset MAIN:errpop1 ;error message mov cx,ERRPOP1_LN mov al,0F9H ;ERRORLEVEL v1.2 jmp Msg_Term ;display, terminate v1.2 Pop_Dir endp ;***************************************************************** ; ; DATA SECTION ; ;***************************************************************** install db 1 ;0 = already installed, 1 = not installed abortmsg db 'Error reading the directory.',13,10 ABORTMSG_LN equ $-abortmsg installmsg db 'PUSHD installed.' INSTALLMSG_LN equ $-installmsg in_dir_msg db 'Valid Directories range between 1 and 6.',13,10 IN_DIR_MSG_LN equ $-in_dir_msg bs_online db 'Invalid Command Line Parameters.',13,10 BS_ONLINE_LN equ $-bs_online not_pushed db 'Parameter exceeds Directory Stack depth.',13,10 NOT_PUSHED_LN equ $-not_pushed full_stack db 'Cannot push Directory -- STACK FULL.',13,10 FULL_STACK_LN equ $-full_stack errpop1 db 'Error popping requested directory.',13,10 ERRPOP1_LN equ $-errpop1 BLANK equ 20h ;' ' DELIMITER equ 2Bh ;'+' UP_LIMIT equ 36h ;'6' LOW_LIMIT equ 31h ;'1' DRIVE_DELIM equ 3Ah ;':' params db ? ;Command line tail params_ln dw ? ;Length of command line tail targ_dir dw ? ;location of storage of targeted directory temp_dir db 67 dup (0) ;temporaray directory storage ;*********************************************************************** ; ; CODE STARTS ; ;*********************************************************************** Start: ;get command line tail cld ;v1.2 I entirely rewrote this section. What a kludge! mov si,offset cmd_line ;command line tail lodsb ;snarf length byte, bump SI ;SI -> first char (probably a space!) xor ah,ah ;clear msb mov cx,ax ;into CX mov params_ln,ax ;save for later push ax ;save length on stack for later jcxz INST_CHK ;CX is 0, branch ;We have a non-null command line mov di,offset MAIN:params ;store since it may get rep movsb ;destroyed INST_CHK: ;first check to see if PUSHD is already installed mov ax,7788h ;signature request mov bx,7789h ;signature request mov si,offset main:signature ;point to signature int 13h ;is it installed ? ASSUME DS:CSEG ;assume TSR's DS v1.2 cmp bx,7788h ;were ax and bx switched ? jne NOTINSTALLED ;no cmp ax,7789h ;were ax and bx switched ? jne NOTINSTALLED ;no ;yes it is installed already mov CS:[install],0 ;don't install it again NOTINSTALLED: ; check for command line parameters pop cx ;get saved cx push DS ;store current DS mov ax,CS ;v1.2 mov DS,ax ;v1.2 ASSUME DS:MAIN ;reminder v1.2 or cx,cx ;check for no parameters jnz ERR1 ;no parameters jmp CHK_STACK ERR1: mov di,offset MAIN:params mov al,BLANK ;remove leading blanks repe scasb dec di inc cx mov si,di ;save position lodsb ;snarf first char, bump SI v1.2 cmp al,BLANK ;space? v1.2 jne ERR2 jmp CHK_STACK ;only blanks on tail ERR2: cmp al,DELIMITER ;is it '+' delimiter? v1.2 je CHK_VALUE jmp CHK_STACK_DIR ;directory on command line CHK_VALUE: ;check parameter values dec cx ;decr for last LODSB ;v1.2 Recoded lodsb ;snarf digit after '+', bump SI cmp al,UP_LIMIT ;above '6'? ja INVALID_DIR ;yep, bogus cmp al,LOW_LIMIT ;below '1'?' jnb FINISH_CHK ;nope, in range ['1'..'6'] INVALID_DIR: ;Invalid directory number -- print error message and exit ; with error code mov dx,offset MAIN:in_dir_msg ;error message mov cx,IN_DIR_MSG_LN mov al,0FFH ;ERRORLEVEL v1.2 jmp Msg_Term ;display, terminate v1.2 FINISH_CHK: ;check for illegal characters on the command line dec cx ;decr cmdline count for digit v1.2 jcxz VALID_DIR mov ah,al ;save digit v1.2 mov al,BLANK ;check for following nonblank repne scasb ;characters mov al,ah ;replace digit v1.2 jcxz VALID_DIR ;illegal characters found on the command line -- print error ; message and exit with and error code mov dx,offset MAIN:bs_online ;error message mov cx,BS_ONLINE_LN mov al,0FEH ;ERRORLEVEL v1.2 jmp Msg_Term ;display, terminate v1.2 VALID_DIR: ;get targeted pushed directory location xor ah,ah ;clear ah v1.2 mov cx,67 ;multiplier v1.2 sub al,'1' ;'0'..'5' range mul cx ;directory offset v1.2 add ax,offset MAIN:push1d ;base directory buffer v1.2 mov targ_dir,ax ;save it v1.2 ;check for validly pushed directory ;CONDITIONS coming into here: ; DS = CS = ES = SS ; DS (installed) on stack pop ax ;TSR's CSEG v1.2 mov DS,ax ;load DS v1.2 mov ES,ax ;and ES v1.2 ASSUME DS:CSEG,ES:CSEG ;assume TSR's v1.2 mov bp,DS:[nextpush] sub bp,67 ;get top of stack location cmp bp,CS:[targ_dir] ;make sure directory stack jge PROCESS_DIRS ;is deep enough for params ;The directory stack is not deep enough for the specified command ; line parameter -- print an error message and exit with error code mov dx,offset MAIN:not_pushed ;error message mov cx,NOT_PUSHED_LN mov al,0FDH ;ERRORLEVEL v1.2 jmp Msg_Term ;display, terminate v1.2 PROCESS_DIRS: ;We have got a valid target directory here, now check to see ; if we are working with the top of the stack. jne NOT_TOP push DS ;put DS (installation) on stack jmp SHORT NEW_CHK_STACK CHK_STACK: ;CONDITIONS coming into here: ;coming from no parameters section of parameter check (NOTINSTALLED) ;CS = DS = SS = ES, DS (installation) on stack pop DS ;set ES = DS = DS (installation) mov ax,DS ;v1.2 mov ES,ax ;v1.2 ASSUME DS:CSEG,ES:CSEG ;reminder v1.2 push DS ;put DS (installation) on stack mov bp,DS:[nextpush] sub bp,67 ;set bp to be top of the stack NEW_CHK_STACK: ;common code here v1.2 mov ax,CS ;v1.2 mov DS,ax ;v1.2 ;CONDITIONS coming into here: ; from PROCESS_DIRS and from fall-through of CHK_STACK ; ES = DS (installation), DS = CS = SS, DS (installation) on stack ;handle the pushing and popping of directories on and off of the ; top of the stack mov si,offset MAIN:temp_dir call Push_Dir ;store current dir in temp_dir pop DS ASSUME DS:CSEG ;assume TSR v1.2 call Pop_Dir ;pop directory in bp push DS mov ax,CS ;v1.2 mov DS,ax ;v1.2 ASSUME DS:MAIN ;reminder v1.2 mov di,bp ;copy temp_dir into top of stack mov cx,67 ;stack position rep movsb mov ES,ax ;AX is still CSEG from above v1.2 jmp GOTNEXTPUSH NOT_TOP: ;CONDITIONS coming into here: ;DS = ES = installation DS, CS = SS, nothing on stack push DS ;store DS mov ax,CS ;v1.2 mov DS,ax ;v1.2 ASSUME DS:MAIN ;reminder v1.2 mov si,offset MAIN:temp_dir call Push_Dir ;store current dir in temp_dir mov bp,targ_dir pop DS ASSUME DS:CSEG ;probably TSR's v1.2 call Pop_Dir ;pop targeted directory mov dx,DS:[nextpush] mov ax,67 ;handy constant v1.2 sub dx,ax ;67 ;set dx to point to top of stack v1.2 mov bx,CS:[targ_dir] ;handle variable here v1.2 STACK_LOOP: ;loop through the directory stack, shifting the the directories ; downward to eliminate the space left by popping a directory ; in the middle of the stack. add bp,ax ;v1.2 cmp bp,dx je LAST_DIR ;found the last directory mov si,bp ;copy contents pointed to by bp mov di,bx ;to targ_dir v1.2 mov cx,ax ;v1.2 rep movsb add bx,ax ;targ_dir,67 v1.2 jmp SHORT STACK_LOOP ;continue LAST_DIR: ;process last directory on stack mov si,bp ;otherwise simply copy mov di,bx ;curr_dir v1.2 mov cx,ax ;67 v1.2 rep movsb mov targ_dir,bx ;update v1.2 mov di,bp ;copy temp_dir to top of stack mov bx,CS ;v1.2 mov DS,bx ;v1.2 ASSUME DS:MAIN ;v1.2 mov si,offset MAIN:temp_dir mov cx,ax ;67 v1.2 rep movsb mov ES,bx ;BX is still CSEG from above v1.2 jmp SHORT GOTNEXTPUSH CHK_STACK_DIR: ;push current directory and change to parameter directory ;CONDITIONS upon entering here: ;CS = DS = ES = SS, DS (installed) on stack pop DS ASSUME DS:CSEG ;assume TSR's v1.2 cmp DS:[nextpush],offset MAIN:push6d ;check for full stack jg STACK_FULL mov si,DS:[nextpush] call Push_Dir ;push current onto stack push DS mov ax,CS ;v1.2 mov DS,ax ;v1.2 ASSUME DS:MAIN ;reminder v1.2 mov bp,1 + offset MAIN:params ;one after blank space ;change to specified directory mov dx,bp ;load DS:dx with directory to set mov ah,3bh ;dos function number int 21h ;set current dir back jc POP_ERR ;check and set default drive mov cx,[params_ln] ;parameter length v1.2 cmp cx,3 ;if cmdline is less than 3 v1.2 jb NO_DRIVE ;can't have drive spec D:\ v1.2 cmp byte ptr DS:[bp+1],DRIVE_DELIM ;drive spec? v1.2 jne NO_DRIVE mov al,DS:[bp] ;v1.2 sub al,'A' ;convert to binary (0=A, 1=B) v1.2 cmp al,1Ah ;'26' == Z as last drive v1.2 jle ACTION sub al,20h ;conversion for lower case drive spec ;v1.2 ACTION: mov dl,al ;NOW put it in DL v1.2 mov ah,0eh ;dos function number int 21h ;set drive jc POP_ERR NO_DRIVE: jmp SHORT OK_DIR POP_ERR: ;Error occcurred while trying to change to specified directory ; print error message and exit with error. mov dx,offset MAIN:errpop1 mov cx,ERRPOP1_LN mov al,0FCH ;ERRORLEVEL v1.2 jmp Msg_Term ;display, terminate v1.2 OK_DIR: ;Increment the location of the nextpush and set DS as ; required by section GOTNEXTPUSH. pop DS add DS:[nextpush],67 mov ax,CS ;v1.2 mov DS,ax ;v1.2 ASSUME DS:MAIN ;reminder v1.2 jmp SHORT GOTNEXTPUSH STACK_FULL: ;The directory stack is full -- print error message and ; exit with error. mov dx,offset MAIN:full_stack ;error message mov cx,FULL_STACK_LN mov al,0FBH ;ERRORLEVEL v1.2 jmp Msg_Term ;display, terminate v1.2 GOTNEXTPUSH: ;CONDITIONS coming into this section: ; CS = DS = SS = ES ASSUME DS:MAIN,ES:MAIN ;reminder v1.2 cmp [install],1 ;should we install it v1.2 je DOINSTALL ;yes ;PUSHD is already installed, exit successfully. mov ax,4c00h int 21h ;no, we are done DOINSTALL: ;PUSHD is not installed, install it by making part of it ; memory resident. assume DS:MAIN ;Save the current int 13h vector. mov ax,3513h ;dos function 35h, vector 13h int 21h ;get the existing vector into ES:bx mov word ptr [savedint13],bx ;save ES:bx mov word ptr [savedint13+2],ES ;save ES:bx ;Set the new int 13h vector to point to routine myint13. mov dx,offset MAIN:myint13 ;point to new routine mov ax,2513h ;dos function 25h, vector 13h int 21h ;set new vector to DS:dx ;Show an installation message on the screen. ; This message can be suppressed by redirecting output to NUL. mov dx,offset MAIN:installmsg mov cx,INSTALLMSG_LN mov bx,1 ;write to stdout mov ah,40h int 21h ;show installation message ;Free up the memory occupied by the envirnoment so it is not ; permanently wasted. mov ES,envseg ;load ES with env seg v1.2 mov ah,49h ;dos function number int 21h ;free the environment memory ;Terminate resident protecting only the first part of this program. ;v1.2 Let the compiler do the computing! mov dx,offset MAIN:ENDRESIDENT add dx,0FH mov cl,4 shr dx,cl ; mov dx,ENDRESIDENT / 16 ;resident code in paragraphs v1.2 mov ax,3100h ;dos function 31h, error code=0 int 21h ;terminate and remain resident ;********************************************************************* ; ; PROCEDURE: Msg_Term ; ; Purpose: This procedure will handle the printing of error messages ; and termination with the correct errorlevel. ; Preconditions: ; DX = error message address. ; CX = error message length. ; AL = ERRORLEVEL. ; Postconditions: ; The program will be terminated with the appropriate ; error message printed on standard error and with the ; the errorlevel stored in al. ; ;*************************************************************************** Msg_Term PROC NEAR ;v1.2 Common routine to display error messages and terminate. push CS ;insure this is so pop DS ASSUME DS:MAIN push ax ;save ERRORLEVEL in AL mov bx,2 ;write to stderr mov ah,40h ;dos function number int 21h ;show error message pop ax ;restore ERRORLEVEL in AL mov ah,4CH ;terminate process int 21h Msg_Term ENDP CSEG ends end Begin ;start execution at Begin