; File MSSKER.ASM
; Edit history:
; [2.29] [jrd]
; Add VT100 emulator, from James Harvey at IUPUI, to IBM version of Kermit.
; Add global entry point vtstat in MSXxxx to do Status display for any
; terminal emulator. Terminal type display is still in MSSSET as usual.
; Fix dynamic screen memory allocation by adding procedure Sbrk and by
; making malloc at least check the ending address of allocated memory
; for extending beyond the datas segment.
; Enlarge stack from 100 to 160 words, for new code.
; Add Kermit commands of Comment to permit comments in Take files and similar
; applications. It consumes the command line without further action.
;
; [2.28 jrd] remove 'C' command to avoid parser error.
; Fully convert i/o to DOS 2 forms, clean errors, add many finer points.
; Allow paths and wild card filenames whereever possible. Get and Receive
; now permit a locally specified path to be prefixed to external filename.
; See msdefs.h for new i/o structure 'filest'.
; Revise RUN procedure to work smoothly with REMOTE and local commands
; for .EXE, .COM, and .BAT files.
;  Joe R. Doupnik  27 Oct 1985
; Modify isfile to find all kinds of files (to avoid reusing system files)
;       and to use its own disk transfer area (filtst structure).
; Move variable 'drives' here from command scanner.
; Reinstate single letter keywords, such as C for connect.
; Make keywords mixed case for reading ease. 26 Dec 1985
; Add printing of cr/lf before executing command.com. Contributed by
;       Larry Afrin. Done on 26 Dec 1985 [jrd]
; Change name of stack segment from 'stack' to 'cstack' to cause linking
;       of segments by alphabetical order to produce the desired order of
;       'code', 'cstack', and 'datas', from low to higher memory. Note
;       that 'datas' must be last because Kermit acquires extra memory
;       dynamically and attaches it to 'datas'. [jrd]
; Add rejection of DELete file(s) if files are hidden, system, vol label,
;       or subdirectory. 9 Jan 1986
; Revise setint to ask DOS for original ^C interrupt vector, as suggested
;       by Jack Bryans. 9 Jan 1986 jrd
; [v2.28]
; Made RUN handle batch files
; When calling command parser for directory, etc., use correct switch char
;       (from by Tony Movshon, NYU)
; JD  15 May 1985

        public  prompt, dosnum, curdsk, fpush, isfile, malloc, sbrk, crun
        include mssdef.h
;******************** Version 2.27 **********************************
; KERMIT, Celtic for "free"
;
; The name "Kermit" is a registered trade mark of Henson Associates, Inc.,
; used by permission.
;
;       Kermit-MS Program Version 2.27, December 6,1984
;
;       Based on the Columbia University KERMIT Protocol.
;
;       Copyright (C) 1982,1983,1984 Trustees of Columbia University
;
;       Daphne Tzoar, Jeff Damens
;       Columbia University Center for Computing Activities
;       612 West 115th Street
;       New York, NY  10025
;
; Special thanks to Frank da Cruz, Bill Catchings, Steve Jensen, Herm Fischer,
; Vace Kundakci, and Bernie Eiben for their help and contributions.

makseg  equ     26H
deffcb  equ     5cH
env     equ     2CH             ; environment address in psp
terma   equ     10              ; termination address in psp
cline   equ     80H             ; offset in psp of command line
namsiz  equ     65              ; Bytes for file name and size.
maxnam  equ     10

CSTACK  SEGMENT PARA STACK 'STACK'      ; Renamed from STACK [jrd]
        dw      150 dup(0)            ; Initialize stack to all zeros.
STK     equ     THIS WORD
CSTACK  ENDS

datas   segment public 'datas'
        extrn   buff:byte, comand:byte, flags:byte, pack:byte, trans:byte
        extrn   prmptr:word, inichk:byte
        extrn   machnam:byte, msfinal:byte, diskio:byte
        public  takadr, taklev, slswit, filtst, drives, monmode

mono	db	'*$'

versio  label   byte
        verdef
        db      cr,lf
        db      '$'
hlpmsg  db      'Type ? for help',cr,lf
        db      '$'

crlf    db      cr,lf,'$'
ermes1  db      cr,lf,'?Unrecognized command$'
ermes2  db      cr,lf,'?Unable to initialize memory$'
ermes3  db      cr,lf,'?Not confirmed$'
ermes4  db      cr,lf,'?Unable to change directory$'
erms30  db      cr,lf,'Passed maximum nesting level for TAKE command$'
erms31  db      cr,lf,'Take-file not found$'
erms34  db      cr,lf,'This command works only for DOS 2.0 and above$'
erms35  db      cr,lf,'Must specify program name$'
erms36  db      cr,lf,'Could not free memory$'
erms37  db      cr,lf,'Unable to execute program$'
tmsg5   db      cr,lf,'[closing log file]',cr,lf,'$' ; [jd]
badnam  db      cr,lf,'?Protected or no such file(s).$' ; [jrd]
filmsg  db      ' File specification with optional path name $'
filwmsg db      ' File specification (possibly wild) with opt path name $'
pthmsg  db      ' Name of new working directory$'       ; [jrd]

tophlp  db      cr,lf,lf
        db      '  Bye      (to remote server)        '
        db      '  Logout   (to remote server)'
        db      cr,lf
        db      '  C or Connect  (to a remote host)   '
        db      '  Push     (go to DOS)'
        db      cr,lf
        db      '  Clear    (key definitions)         '
        db      '  Quit     (leave Kermit)'
        db      cr,lf
        db      '  Close    (logging file)            '
        db      '  Receive  (opt local filename)'
        db      cr,lf
        db      '  Comment  (text is ignored)         '
        db      '  Remote   (prefix for commands)'
        db      cr,lf
        db      '  CWD      (change dir &/or disk)    '
        db      '  Run      (a program)'
        db      cr,lf
        db      '  Define   (a command macro)         '
        db      '  Send     (local file   new name)'
        db      cr,lf
        db      '  Delete   (a file)                  '
        db      '  Server   (become a local server)'
        db      cr,lf
        db      '  Directory                          '
        db      '  Set      (most things)'
        db      cr,lf
        db      '  Do       (a macro)                 '
        db      '  Show     (various definitions)'
        db      cr,lf
        db      '  Exit     (leave Kermit)            '
        db      '  Space    (left on current disk)'
        db      cr,lf
        db      '  Finish   (to remote server)        '
        db      '  Status   (show main conditions)'
        db      cr,lf
        db      '  Get      (remote file opt new name)'
        db      '  Take     (do a command file)'
        db      cr,lf
        db      '  Help     (show this list)          '
        db      '  Type     (a file)'
        db      cr,lf
        db      '  Local    (opt command prefix)      '
        db      '  Version  (show Kermit',27H,'s id)'   ; 27H = single quote
        db      cr,lf
        db      '  Log      (session to a file)       '
        db      cr,lf,'$'
lochlp  db      cr,lf,'CWD path'
        db      cr,lf,'Delete file'
        db      cr,lf,'Directory [filespec]'
        db      cr,lf,'Space remaining on current disk'
        db      cr,lf,'Run program'
        db      cr,lf,'Push to DOS'
        db      cr,lf,'Type local file'
        db      '$'

        ; COMND tables

yestab  db      2
        mkeyw   'no',0
        mkeyw   'yes',1

comtab  db      32
        mkeyw   'Bye',bye
        mkeyw   'C',telnet
        mkeyw   'Clear',clear
        mkeyw   'Close',clscpt
        mkeyw   'Comment',rskp
        mkeyw   'Connect',telnet
        mkeyw   'CWD',cwd
        mkeyw   'Define',dodef
        mkeyw   'Delete',delete
        mkeyw   'Directory',direct
        mkeyw   'Do',docom
        mkeyw   'Exit',exit
        mkeyw   'Finish',finish
        mkeyw   'Get',get
        mkeyw   'Help',help
        mkeyw   'Local',lclcmd
        mkeyw   'Log',setcpt
        mkeyw   'Logout',logout
        mkeyw   'Push',dopush
        mkeyw   'Quit',exit
        mkeyw   'Receive',read
        mkeyw   'Remote',remote
        mkeyw   'Run',run
        mkeyw   'Send',send
        mkeyw   'Server',server
        mkeyw   'Set',setcom
        mkeyw   'Show',showcmd
        mkeyw   'Space',chkdsk
        mkeyw   'Status',status
        mkeyw   'Take',take
        mkeyw   'Type',typec
        mkeyw   'Version',prvers

loctab  db      7
        mkeyw   'CWD',cwd
        mkeyw   'Delete',delete
        mkeyw   'Directory',direct
        mkeyw   'Push',dopush
        mkeyw   'Run',run
        mkeyw   'Space',chkdsk
        mkeyw   'Type',typec

shotab  db      2
        mkeyw   'Key',shokey
        mkeyw   'Macros',shomac

; Program storage.

ssave   dw      ?               ; Original SS when doing Command.com [jrd]
in3ad   dw      0,0             ; Original break interrupt addresses. [25]
curdsk  db      0               ; Current disk.
origd   db      0               ; Original disk.
orgdir  db      64 dup (?)      ; original directory on original disk [jrd]
drives  db      ?               ; number of disk drives on system. [jrd]
taklev  db      0               ; Take levels. [25t]
takadr  dw      takstr-(size takinfo) ; Pointer into structure. [25t]
psp     dw      ?
takstr  db      (size takinfo) * maxtak dup(?)
ininm2  db      'MSKERMIT.INI',0        ; init file name for 2.0
nambuf  db      maxnam * namsiz dup (?)
filtst  filest  <>              ; file structure for procedure isfile. [jrd]
cmdnam  db      namsiz dup (?)
exefcb  db      fcbsiz dup (?)
exefcb2 db      fcbsiz dup (?)
exearg  dw      ?               ; segment addr of environment (filled in below)
        dd      0               ; ptr to cmd line (filled in below)
        dd      exefcb          ; default fcb
        dd      exefcb2         ; second default fcb
delcmd  db      ' del ',0       ; delete command [jrd]
dircmd  db      ' dir ',0       ; directory command [jrd]
dirnam  db      64 dup (?)
chkdcmd db      'chkdsk.com',0  ; space command [jrd]
typcmd  db      ' type ',0      ; type command [jrd]
dosnum  db      ?               ; dos version number
pthnam  db      'PATH='
pthlen  equ     $-pthnam
pthbuf  db      80 dup (?)      ; buffer for path definition.
defpth  db      '\', 80 dup (?) ; buffer for default path
slashc  db      ' '
slswit  db      '/'
        db      'c ',0
cmspnam db      'COMSPEC='
cmsplen equ     $-cmspnam
cmspbuf db      '\command.com',0 ; default name
        db      30 dup (0)      ; some additional space
tfile   db      64 dup (?)      ; temp space for file names.
eexit   db      cr,'exit',cr
leexit  equ     $-eexit
mfmsg   db      '?Not enough memory to run Kermit$'
mf7msg  db      '?Attempted to allocate a corrupted memory area$'
monmode	db	0
datas   ends                    ; End data segment

code    segment public
        public  takrd
start   proc  far
        extrn   cmblnk:near, locate:near, logout:near
        extrn   bye:near, telnet:near, finish:near, comnd:near
        extrn   read:near, remote:near, send:near, status:near, get:near
        extrn   dodisk:near, serrst:near, setcom:near
        extrn   clscpi:near, clscpt:near, getbaud:near
        extrn   dodef:near, setcpt:near, docom:near
        extrn   server:near, lclini:near, shokey:near, shomac:near
        extrn   packlen:near, clrdef:near, strlen:near, strcpy:near
        extrn   strcat:near, prtasz:near
        assume  cs:code, ds:datas, ss:cstack, es:nothing        ;[jrd]


        mov ax,datas           ; Initialize DS.
        mov ds,ax
        sub ax,ax
        mov psp,es              ; remember psp address [jrd]

        mov ah,prstr
        mov dx,offset machnam   ; print machine name
        int dos
        mov ah,prstr            ; Print the version header.
        mov dx,offset versio
        int dos
        mov ah,prstr
        mov dx,offset hlpmsg
        int dos

        mov ah,setdma           ; Set disk transfer address.
        mov dx,offset buff
        int dos

        call getbaud            ; Get the baud rate.
        call dodisk             ; See how many disk drives we have.
        call setint
        mov ah,gcurdsk          ; Get current disk.
        int dos
        inc al                  ; We want 1 == A (not zero).
        mov curdsk,al
        mov origd,al            ; Remember original disk we started on.
        mov dx,ds
        mov es,dx               ; set es to ds segment
        mov si,offset orgdir    ; place for directory path w/o drive code[jrd]
        add al,'A'-1            ; make al alphabetic disk drive again [jrd]
        mov [si],al             ; put it into original path descriptor [jrd]
        inc si
        mov byte ptr [si],':'   ; add drive specifier too [jrd]
        inc si
        mov byte ptr [si],'\'   ; add root indicator as well [jrd]
        inc si
        mov ah,gcd              ; get current directory (path really) [jrd]
        xor dl,dl               ; use current drive
        int dos                 ; do it [jrd]
        mov ah,dosver
        int dos
        mov dosnum,al           ; remember dos version
        mov es,psp
        mov ax,es:[env]         ; pick up environment address
        push ax
        call getpath            ; get the path from the environment
        pop ax                  ; get environment back
        call getcsp             ; get comspec from environment as well
        call memini             ; init our memory usage
        call lclini             ; do local initialization
        call gcmdlin            ; read command line
        call rdinit             ; read kermit init file
        call packlen            ; Packet length in case do server comand.
        mov ah,gswitch
        mov al,0                ; pick up switch character
        int dos
        mov slswit,dl
				; pick up the monitor type
        mov ah, 0fh             ; read video mode
        int 10h                 ; int screen
        mov monmode,al          ; get mode (0 - ??)

; This is the main KERMIT loop.  It prompts for and gets the users commands.

kermit: mov ax,ds
        mov es,ax               ; make sure this addresses data segment
        mov dx,prmptr           ; get prompt
        call prompt             ; Prompt the user.
        mov pack.state,0        ; Clear the state.
        mov flags.cxzflg,0      ; Reset each itme.
        mov ah,inichk           ; Original or set checksum length.
        mov trans.chklen,ah     ; Reset just in case.
        mov dx,offset comtab
        mov bx,offset tophlp
        mov comand.cmcr,1       ; Allow bare CR's.
        mov ah,cmkey
        call comnd
         jmp kermt2
        mov comand.cmcr,0       ; Not anymore.
        call bx                 ; Call the routine returned.
         jmp kermt3
        cmp flags.extflg,0      ;  Check if the exit flag is set.
        jne krmend              ;  If so jump to KRMEND.
        jmp kermit              ; Do it again.

kermt2: mov dx,offset ermes1    ;  Give an error.
        jmp short kermt4

kermt3: mov dx,offset ermes3    ;  Give an error.
kermt4: cmp flags.cxzflg,'C'    ; some sort of abort?
        je kermit               ; yes, don't print error message.
        mov ah,prstr
        int dos
        mov flags.nmoflg,0      ; Reset filename override flag.
        mov flags.getflg,0      ; May as well do this one.
        mov flags.cmrflg,0      ; This one too.
        jmp kermit

krmend: call serrst             ; Just in case the port wasn't reset. [21c]
        mov dl,origd            ; Original disk drive.
        dec dl                  ; Want A == 0.
        mov ah,seldsk           ; Reset original disk just in case.
        int dos
        mov dx,offset orgdir    ; restore original directory [jrd]
        mov ah,chdir
        int dos
        mov     dx,offset in3ad ; restore control C interrupt vector [jrd]
        mov     al,23H          ; interrupt 23H
        mov     ah,25H          ; set interrupt vector
        int     dos             ; ah, that's better.
        mov     ah,4cH          ; terminate process     [jrd]
        mov     al,0            ; return error level of 0       [jrd]
        int     dos
        ret

START   ENDP

; change working directory
cwd     proc    near
        mov     ah,cmfile       ; [jrd]
        mov     dx,offset tfile
        mov     bx,offset pthmsg
        call    comnd
         jmp    r
        mov     dx,offset tfile
        mov     ah,chdir
        int     dos
        jnc     cwd1
        mov     dx,offset ermes4
        mov     ah,prstr
        int     dos
        jmp     rskp
cwd1:   mov     bx,dx                   ; change of drives, if req'd [jrd]
        cmp     byte ptr [bx+1],':'     ; was a drive specified?
        jne     cwd3                    ; ne = no
        mov     dl,[bx]                 ; get the drive letter
        and     dl,5FH                  ; make upper case
        sub     dl,'A'                  ; convert to A = 0, etc
        mov     ah,seldsk
        int     dos                     ; change disks
        inc     dl                      ; count A = 1 internally
        mov     curdsk,dl               ; and store it
cwd3:   jmp     rskp
cwd     endp

; clear macro definitions
clear   proc    near
        mov     ah,cmcfm
        call    comnd
         jmp    r
        call    clrdef
        jmp     rskp
clear   endp

; This is the 'EXIT' command.  It leaves KERMIT and returns to DOS.

EXIT    PROC    NEAR
        mov ah,cmcfm
        call comnd              ; Get a confirm.
         jmp r
        test    flags.capflg,0FFH       ; capturing?
        jz      exit1                   ; no, keep going
        mov     dx,offset tmsg5
        mov     ah,prstr
        int     dos
        call    clscpi
         nop                            ; this skip returns...
         nop
         nop
exit1:
        mov flags.extflg,1      ;  Set the exit flag.
        jmp rskp                ; Then return to system.
EXIT    ENDP


; This is the 'HELP' command.  It gives a list of the commands.

HELP    PROC    NEAR
        mov ah,cmcfm
        call comnd              ; Get a confirm.
         jmp r
        mov ah,prstr            ; Print a string to the console.
        mov dx,offset tophlp    ; The address of the help message.
        int dos
        jmp rskp
HELP    ENDP

lclcmd  proc    near
        mov ah,cmkey
        mov dx,offset loctab
        mov bx,offset lochlp
        call comnd
         jmp r
        call bx
        nop
        nop
        nop
        jmp rskp
lclcmd  endp

; Don't ignore ^C when in debug mode.
; Revised to ask DOS for original interrupt vector contents, as suggested by
; Jack Bryans. 9 Jan 1986 jrd
SETINT  PROC    NEAR
        push es                 ; save registers
        push bx
        mov al,23H              ; desired interrupt vector (^C)
        mov ah,35H              ; Int 21H, function 35H = Get Vector. Sorry.
        int dos                 ; get vector in es:bx
        mov in3ad,bx            ; save main address of original vector
        mov in3ad+2,es          ;   and its offset.
        push ds                 ; save ds around next DOS call.
        mov ax,cs               ; compose full address of our ^C routine.
        mov ds,ax               ; Offset is the code segment.
        mov dx,offset intbrk    ;   and main address is intbrk.
        mov al,23H              ; On ^C, goto intbrk.
        mov ah,25H              ; set interrupt address from ds:dx
        int dos
        pop ds
        pop bx
        pop es
        ret
SETINT  ENDP

; TAKE commands from a file, and allow a path name
TAKE    PROC    NEAR
        cmp taklev,maxtak               ; Hit our limit?
        jl take1                        ; Continue if still OK.
        mov ah,prstr
        mov dx,offset erms30            ; Complain.
        int dos
        ret
take1:  mov di,takadr
        add di,size takinfo
        push di
        mov ah,cmtxt
        lea bx,[di].takbuf              ; convenient place to parse name into
        mov dx,offset filmsg            ; Help in case user types "?".
        call comnd
         pop di
         ret
         nop
        pop di                          ; restore frame address
        cmp ah,0
        je take2                        ; empty, complain.
        push di                         ; keep it on stack.
        lea si,[di].takbuf              ; get buffer back
        mov bl,ah                       ; length of thing parsed
        mov bh,0
        mov byte ptr [bx+si],0          ; make it asciz
        mov ax,si                       ; point to name again
        call spath                      ; is it around?
        pop di                          ; need this back
        jc take2                        ; no, go complain
        mov dx,ax                       ; point to name from spath
        mov ah,open2                    ; 2.0 open call
        mov al,0                        ; open for reading
        int dos
        jnc take3                       ; open ok, keep going
take2:  mov ah,prstr
        mov dx,offset erms31
        int dos
        ret
take3:  inc taklev
        mov takadr,di
        mov word ptr [di].takhnd,ax     ; save file handle [jrd]
        mov byte ptr [di].taktyp,0feh   ; mark as 2.0 file handle [jrd]
        mov bx,ax                       ; need descriptor here
        mov ah,lseek
        mov al,2
        mov cx,0
        mov dx,cx                       ; seek 0 bytes from end
        int dos
        mov [di].takcnt,ax
        mov [di].takcnt+2,dx            ; store length
        mov ah,lseek
        mov al,0
        mov cx,0
        mov dx,cx                       ; now seek back to beginning
        int dos
        cmp flags.takflg,0              ; echoing commands? [jrd]
        je take4                        ; e = no. [jrd]
        mov ah,prstr
        mov dx,offset crlf
        int dos
take4:  call takrd              ; Get a buffer full of data.
        jmp rskp
TAKE    ENDP

TAKRD   PROC    NEAR
        push bx
        push cx
        push dx
        mov bx,takadr
        push bx                         ; save frame address
        lea dx,[bx].takbuf              ; buffer to read into
        mov cx,dmasiz                   ; # of bytes to read
        mov ah,readf2                   ; 2.0 read call
        mov bx,word ptr [bx].takhnd     ; file handle is stored here
        int dos
        pop bx                          ; restore frame address
        jmp takrd2                      ; rejoin common exit

takrd2: mov [bx].takchl,dmasiz
        lea ax,[bx].takbuf
        mov [bx].takptr,ax
        pop dx
        pop cx
        pop bx
        ret

TAKRD   ENDP

; copy the path into pthbuf
; enter with ax/ environment segment address
; works in 2.0 only.
getpath proc    near
        push    es
        mov     bx,ds
        mov     es,bx                   ; address data segment
        mov     bx,offset pthnam        ; thing to find
        mov     cx,pthlen               ; length of it
        mov     dx,offset pthbuf        ; place to put it
        mov     byte ptr pthbuf,0       ; initialize to null...
        call    getenv                  ; get environment value
        pop     es
        ret                             ; and return
getpath endp

; copy the comspec into cmspbuf
; enter with ax/ environment segment address
; works in 2.0 only.
getcsp  proc    near
        push    es
        mov     bx,ds
        mov     es,bx                   ; address data segment
        mov     bx,offset cmspnam       ; thing to find
        mov     cx,cmsplen              ; length of it
        mov     dx,offset cmspbuf       ; place to put it
        call    getenv                  ; get environment value
        pop     es
        ret                             ; and return
getcsp  endp

; find a path variable.  Enter with ax/ environment segment,
; bx/ variable to find (incl =), cx/ length of variable name,
; dx/ address to store value at.
; The buffer given in dx is unchanged if the variable isn't found
getenv  proc    near
        push    ds
        push    es
        mov     es,ax                   ; address segment
        mov     di,0                    ; offset in segment
geten1: cmp     es:byte ptr [di],0      ; end?
        je      geten4                  ; yes, forget it
        push    cx                      ; save counter
        push    di                      ; and offset
        mov     si,bx
        repe    cmpsb                   ; is it the one?
        pop     di
        pop     cx                      ; restore these
        je      geten2                  ; found it, break loop
        push    cx                      ; preserve again
        mov     cx,0ffffh               ; bogus length
        mov     al,0                    ; marker to look for
        repne   scasb                   ; search for it
        pop     cx                      ; restore length
        jmp     geten1                  ; loop thru rest of environment
geten2: add     di,cx                   ; skip to definition
        mov     si,di                   ; this is source
        mov     di,dx                   ; destination as given
        mov     ax,ds
        mov     bx,es
        mov     ds,bx
        mov     es,ax                   ; exchange segment regs for copy
geten3: lodsb                           ; get a byte
        stosb                           ; drop it off
        cmp     al,0                    ; end of string
        jne     geten3                  ; no, go on
geten4: pop     es
        pop     ds                      ; restore registers
        ret                             ; and return
getenv  endp

; put kermit.ini onto take stack if it exists.  Just like
; the take command, except it doesn't read a filename.

rdinit  proc    near            ; read kermit init file...

rdini4: mov ax,offset ininm2    ; name to try
        push bx
        call spath              ; can we find it?
        pop di
        jc rdini6               ; no, forget it, go use it
        mov dx,ax               ; point to name
        mov ah,open2            ; 2.0 open function
        mov al,0                ; for reading...
        int dos
        jc rdini6               ; can't open, forget it

rdini5: inc taklev              ; bump take level
        add takadr,size takinfo
        mov di,takadr           ; get current frame ptr
        mov word ptr [di].takhnd,ax     ; save file handle [jrd]
        mov byte ptr [di].taktyp,0feh   ; mark as a handle [jrd]
        mov bx,ax                       ; move file ptr
        mov ah,lseek
        mov al,2
        mov cx,0
        mov dx,0                        ; seek to end of file
        int dos
        mov [di].takcnt,ax              ; copy file size
        mov [di].takcnt+2,dx            ; into structure
        mov al,0
        mov ah,lseek
        mov cx,0
        mov dx,0
        int dos                         ; seek back to beginning
        cmp flags.takflg,0
        je rdini3
        mov ah,prstr
        mov dx,offset crlf
        int dos
rdini3: call takrd                      ; Get a buffer full of data.
rdini6: ret                             ; no init file, just return
rdinit  endp

; get command line into a macro buffer.

gcmdlin proc    near
        push    ds
        push    es
        cld
        mov     es,psp                  ; address psp
        mov     ch,0
        mov     cl,es:[cline]           ; length of cmd line
        mov     di,cline+1              ; point to actual line
        mov     al,' '
        jcxz    gcmdl3                  ; no command line, forget it.
        repe    scasb                   ; skip over spaces
        je      gcmdl3                  ; all spaces, forget it
        mov     si,di                   ; this is first non-space
        dec     si                      ; pre-incremented...
        inc     cx
        inc     taklev                  ; bump take level
        add     takadr,size takinfo     ; address new take frame
        mov     bx,takadr
        mov     byte ptr [bx].taktyp,0ffh ; mark as a macro [jrd]
        push    cx                      ; save length
        push    ds                      ; and segment
        lea     di,[bx].takbuf          ; into take buffer
        mov     ax,ds
        mov     ds,psp
        mov     es,ax                   ; switch segments for copy
gcmdl1: lodsb                           ; get a byte
        cmp     al,','                  ; comma?
        jne     gcmdl2                  ; no, keep going
        mov     al,cr                   ; convert to cr
gcmdl2: stosb                           ; deposit it
        loop    gcmdl1                  ; copy whole cmd
        pop     ds                      ; restore segment
        mov     si,offset eexit         ; something to tack onto end
        mov     cx,leexit               ; length of it
        rep     movsb                   ; copy it in
        pop     cx                      ; restore len
        add     cx,leexit               ; count wnat we added

        lea     ax,[bx].takbuf
        mov     [bx].takptr,ax          ; init buffer ptr
        mov     [bx].takchl,cl          ; chars remaining
        mov     [bx].takcnt,cx          ; and all chars
        mov     [bx].takcnt+2,0         ; clear high order
gcmdl3: pop     es
        pop     ds
        ret
gcmdlin endp

;       This routine prints the prompt and specifies the reparse address.

PROMPT  PROC  NEAR
        mov comand.cmprmp,dx    ; save the prompt
        pop bx                  ; Get the return address.
        mov comand.cmrprs,bx    ; Save as addr to go to on reparse.
        mov comand.cmostp,sp    ; Save for later restoral.
        push bx                 ; Put it on the stack again.
        mov bx,offset comand.cmdbuf
        mov comand.cmcptr,bx    ; Initialize the command pointer.
        mov comand.cmdptr,bx
        mov ah,0
        mov comand.cmaflg,ah    ; Zero the flags.
        mov comand.cmccnt,ah
        mov comand.cmsflg,0FFH
        cmp flags.takflg,0      ; look at take flag
        jne promp1              ; supposed to echo, skip this check...
        cmp taklev,0            ; inside a take file?
        je promp1               ; no, keep going
        ret                     ; yes, return
promp1: mov ah,prstr
        mov dx,offset crlf
        int dos
        mov ah,prstr            ; Print the prompt.
        mov dx,comand.cmprmp
        int dos
        ret
PROMPT  ENDP

; Erase specified file(s). Add protection of ignore hidden, subdir, volume
; label and system files. 9 Jan 86 [jrd]
DELETE  PROC    NEAR            ; revised for DOS 2.0, incl paths & ?* [jrd]
        mov si,offset delcmd    ; del command
        mov di,offset dirnam
        call strcpy
        mov dx,offset dirnam
        call strlen             ; get its length
        add di,cx               ; point at terminator
        mov comand.cmcr,0       ; Filename must be specified.
        mov ah,cmtxt            ; parse with cmtxt so we can have paths...
        mov bx,di               ; where to place the file specs
        mov dx,offset filwmsg   ; In case user wants help.
        call comnd
         jmp r
        mov byte ptr [bx],0     ; plant terminator
        mov dx,offset delcmd    ; get length of prefix (del )
        call strlen
        mov ax,offset dirnam    ; command line so far
        add ax,cx               ; bump address to filename field
        call isfile             ; and ask if file exists & what kind it is
        jc delet2               ; c = no such file, complain
        test byte ptr filtst.dta+21,1EH; attribute bits: is file protected?
        jz delet3               ; z = no. go ahead.
delet2: mov ah,prstr
        mov dx,offset badnam    ; give error message
        int dos
        jmp rskp                ; and ignore this command
delet3: mov si,offset dirnam    ; del cmd
        jmp crun                ; join run cmd from there.
DELETE  ENDP

CHKDSK  PROC    NEAR
        mov ah,cmcfm
        call comnd
         jmp r
        mov si,offset chkdcmd           ; point to cmd
        jmp crun                        ; and go execute it nicely
CHKDSK  ENDP

; Get directory listing.
DIRECT  PROC    NEAR
        mov si,offset dircmd    ; dir command
        mov di,offset dirnam
        call strcpy
        mov dx,offset dirnam
        call strlen             ; get its length
        add di,cx               ; point at terminator
        mov ah,cmtxt            ; parse with cmtxt so we can have paths...
        mov bx,di               ; next available byte
        mov dx,offset filwmsg   ; In case user wants help.
        call comnd
         jmp r
        mov byte ptr [bx],0     ; plant terminator
        mov si,offset dirnam
        jmp crun                ; join run cmd from there.
DIRECT  ENDP

; the version command - print our version number
prvers  proc    near
        mov     ah,cmcfm
        call    comnd
         jmp    r
        mov     ah,prstr
        mov     dx,offset crlf
        int     dos
        mov     ah,prstr
        mov     dx,offset machnam       ; print machine name
        int     dos
        mov     ah,prstr            ; Print the version header.
        mov     dx,offset versio
        int     dos
        jmp     rskp
prvers  endp

; the type command - type out a file
typec   proc    near
        mov si,offset typcmd    ; type command
        mov di,offset dirnam
        call strcpy
        mov dx,offset dirnam
        call strlen             ; get its length
        add di,cx               ; point at terminator
        mov ah,cmtxt            ; parse with cmtxt so we can have paths...
        mov bx,di               ; next available byte
        mov dx,offset filmsg    ; In case user wants help.
        call comnd
         jmp r
        mov byte ptr [bx],0     ; plant terminator
        mov si,offset dirnam
        jmp crun                ; join run cmd from there.
typec   endp

; PUSH to DOS (run another copy of Command.com or equiv)
; entry fpush (fast push...) pushes without waiting for a confirm.
; returns rskp.
dopush  proc    near
dopus1: mov     ah,cmcfm
        call    comnd
         jmp    r
fpush:  mov     si,offset dirnam        ; a dummy buffer
        mov     byte ptr [si],0         ; plant terminator
        jmp     short run4              ; go run it
dopush  endp

CRUN    proc    near
; crun - run an arbitrary program.      Revised by [jrd]
; Enter with ordinary asciiz command in si (such as Dir *.asm). cx is free.
; Puts ' \c ' into nambuf and appends string above. Leaves si = start of nambuf
        mov ax,ds
        mov es,ax               ; address dest segment
        push si                 ; save incoming string pointer
        mov si,offset slashc
        mov di,offset nambuf
        call strcpy
        pop si
        call strcat             ; "  \c "+incoming text
        mov si,offset nambuf    ; point to command
        jmp short run3          ; and join run code
CRUN    ENDP

RUN     PROC    NEAR
                                ; prompt for program to be run
        cmp dosnum,0
        jne run1
        mov ah,prstr
        mov dx,offset erms34    ; Complain.
        int dos
        jmp rskp
run1:   mov si,offset slashc    ; do prepend " /c "
        mov di,offset nambuf
        call strcpy
        mov dx,di               ; get its length
        call strlen
        add di,cx               ; point at the terminator
        mov ah,cmtxt            ; Get program name.
        mov bx,di               ; place for user's text
        mov dx,offset filmsg    ; In case user wants help.
        call comnd
         nop
         nop
         nop
        cmp ah,0
        jne run2
        mov ah,prstr
        mov dx,offset erms35
        int dos
        jmp rskp
run2:   mov byte ptr [bx],0             ; plant terminator
        mov si,offset nambuf

; come here with asciiz main command prepended with " \c ".
; Append a c/r and a null terminator and then ask command.com to do it.
run3:
        mov ah,prstr            ; output crlf before executing comnd. [lba]
        mov dx,offset crlf      ; [lba]
        int dos                 ; display it. [lba]
        mov dx,si               ; si points to main command
        call strlen             ; get its length into cx
        push bx
        mov bx,dx
        add bx,cx
        mov byte ptr [bx],cr    ; end string with a c/r for dos.
        inc bx
        inc cx                  ; count the c/r
        mov byte ptr [bx],0     ; and terminate [jrd]
        pop bx
        dec cx                  ; deduct leading space (byte count here)
        mov [si],cl             ; put length of argument here
run4:   mov exearg+2,si         ; pointer to argument string
        mov exearg+4,ds         ; segment of same
        mov es,psp              ; point to psp again
        mov ax,es:[env]         ; get environment ptr
        mov exearg,ax           ; put into argument block
        mov ax,ds
        mov es,ax               ; put es segment back
        mov ax,offset cmspbuf   ; always use command.com [jrd]
        mov dx,ax               ; point to command name
        mov al,0                ; load and execute...
        mov ah,exec
        mov bx,offset exearg    ; and to arguments
        mov ssave,sp            ; save stack ptr
        int dos                 ; go run the program
        mov ax,seg datas
        mov ds,ax               ; reset data segment
        mov es,ax               ; and extra segment [jrd]
        mov ax,seg cstack       ; [jrd]
        mov ss,ax               ; and stack segment
        mov sp,ssave            ; restore stack ptr
        pushf                   ; save flags
        mov ah,setdma
        mov dx,offset buff
        int dos                 ; restore dma address!!
        popf                    ; recover flags
        jc run8                 ; error, handle.
        jmp rskp                ; ok, return
run8:   mov ah,prstr
        mov dx,offset erms37
        int dos
        jmp rskp
RUN     ENDP

; the show command
showcmd proc    near
        mov     ah,cmkey
        mov     dx,offset shotab
        xor     bx,bx                   ; no canned help
        call    comnd
         jmp    r
        call    bx                      ; call the handler
         jmp    r
        jmp     rskp                    ; and return
showcmd endp

intbrk: cmp flags.debug,1       ; Debug mode?
        je intb1                ; Yes, then don't ignore the ^C.
        push ax
        push ds
        mov ax,seg datas
        mov ds,ax
        mov flags.cxzflg,'C'    ; Say we saw a ^C.
        mov pack.state,'A'      ; Set the state to abort.
        pop ds
        pop ax
        iret
intb1:  jmp in3ad               ; Original break interrupt address.


SPATH   proc    near
; enter with ax/ ptr to file name.  Searches path for given file,
; returns with ax/ ptr to whole name, or carry on if file isn't
; to be found.
        push    es
        mov     bx,ds
        mov     es,bx                   ; address data segment
        mov     bx,ax                   ; convenient place to keep this
        call    isfile                  ; does it exist as it is?
        mov     ax,bx                   ; ifso, just return original name
        jc      spath0                  ; nope...
        pop     es
        ret
spath0: mov     si,ax
        mov     di,offset tfile         ; place to copy to
        mov     dl,0                    ; no '\' seen yet
spath1: lodsb
        stosb
        cmp     al,'/'                  ; contain path characters?
        je      spath1a
        cmp     al,'\'
        jne     spath2                  ; no, keep going
spath1a:mov     dl,1                    ; remember we've seen them
spath2: or      al,al
        jnz     spath1                  ; copy name in
        or      dl,dl                   ; look at flag
        jz      spath3                  ; no path, keep looking
        jmp     spath9                  ; embedded path, fail

spath3: mov     si,offset pthbuf        ; path definition
        cmp     byte ptr [si],0         ; empty path?
        jne     spath4                  ; no, keep going
        mov     ah,gcd                  ; get current dir
        mov     dl,0                    ; for default drive
        mov     si,offset defpth+1      ; place to put it
        int     dos
        mov     si,offset defpth        ; point to the path
spath4: cmp     byte ptr [si],0         ; null, exit loop
        je      spath9
        mov     di,offset tfile         ; place to put name
spath5: lodsb                           ; get a byte
        cmp     al,';'                  ; end of this part?
        je      spath7                  ; yes, break loop
        cmp     al,0                    ; maybe end of string?
        jne     spath6                  ; no, keep going
        dec     si                      ; back up over it
        jmp     short spath7            ; and break loop
spath6: stosb                           ; else stick in dest string
        jmp     spath5                  ; and continue
spath7: push    si                      ; save this ptr
        mov     si,bx                   ; this is user's file name
        cmp     byte ptr [di-1],'/'     ; does it end with switch char?
        je      spath8                  ; yes, don't put one in
        mov     al,'\'                  ; how about this one?
        cmp     byte ptr [di-1],al
        je      spath8                  ; yes, don't put it in.
        stosb                           ; else add one
spath8: lodsb
        stosb
        or      al,al
        jnz     spath8                  ; copy rest of name
        pop     si                      ; restore pos in path def
        mov     ax,offset tfile
        call    isfile                  ; is it a file?
        jc      spath4                  ; no, keep looking
        mov     ax,offset tfile
        pop     es
        ret                             ; return success (carry off)
spath9: pop     es                      ; restore this
        stc                             ; no file found
        ret
spath   endp


ISFILE  PROC    NEAR
; Enter with ds:ax pointing at asciiz filename string.
; Returns carry set if the file pointed to by ax does not exist, else reset.
; Does a serach-for-first to permit paths and wild cards.
; Examines All kinds of files (ordinary, subdirs, vol labels, system,
;  and hidden). Upgraded to All kinds on 27 Dec 1985  [jrd]
; All registers are preserved.

        push    dx                      ; save regs
        push    cx
        push    ax
        mov     byte ptr filtst.dta+21,0 ; clear old attribute bits
        mov     byte ptr filtst.name,0  ; clear any old filenames
        mov     cx,3fH                  ; look at all kinds of files
        mov     dx,offset filtst.dta    ; own own temporary dta
        mov     ah,setdma               ; set to new dta
        int     dos
        pop     dx                      ; get ax (filename string ptr)
        push    dx                      ; save it again
        mov     ah,first2               ; search for first
        int     dos
        mov     dx,offset buff          ; reset dma
        mov     ah,setdma
        int     dos
        cmp     byte ptr filtst.name,0  ; did DOS fill in a name?
        jne     isfil1                  ; nz = yes
        stc                             ; else set carry flag bit
isfil1: pop     ax
        pop     cx
        pop     dx
        ret                             ; DOS sets carry if file not found.
ISFILE  ENDP

; initialize memory usage by returning to DOS anything past the end of kermit
memini  proc    near
        push    es
        mov     es,psp          ; address psp segment again
        mov     bx,offset msfinal + 15 ; end of pgm + roundup
        mov     cl,4
        shr     bx,cl           ; compute # of paragraphs in last segment
        mov     ax,seg datas    ; last segment
        sub     ax,psp          ; minus beginning
        add     bx,ax           ; # of paragraphs occupied
        mov     ah,setblk
        int     dos
         jc     memin1
        pop     es
        ret
memin1: pop     es
        mov     dx,offset ermes2
        mov     ah,prstr
        int     dos             ; complain
        jmp     krmend          ; and just exit...
memini  endp

; allocate memory.  Passed a memory size in ax, allocates that many
; bytes (actually rounds up to a paragraph) and returns its segment in ax.
; The memory is NOT initialized. Warning: this routine should be avoided in
; new work because it assumes the newly allocated memory is contiguous
; with the Datas segment of Kermit. Added check on end of space. [jrd]
malloc  proc    near
        mov     bx,ax
        add     bx,15           ; round up
        mov     cl,4
        shr     bx,cl           ; convert to # of paragraphs
        mov     ah,alloc
        int     dos
        jc      mfatal          ; fatal to fail now...
        mov     cx,ds           ; refer addresses to DATAS SEGMENT
        sub     ax,cx           ; make segment an offset within datas
        add     ax,bx           ; check the ending, not the start! [jrd]
        cmp     ax,1000h        ; too big?
        jae     mfatal          ; yup
        mov     cl,4
        shl     ax,cl           ; make into offset
        ret                     ; and return
mfatal: mov     dx,offset mfmsg ; assume not enough memory (ax = 8).
        cmp     ax,7            ; corrupted memory (ax = 7)?    [jrd]
        jne     mfata1          ; ne = no.
        mov     dx,offset mf7msg        ; corrupted memory found. [jrd]
mfata1: mov     ah,prstr
        int     dos
        jmp     krmend
malloc  endp

; allocate memory.  Passed a memory size in ax, allocates that many
; bytes (actually rounds up to a paragraph) and returns its SEGMENT in ax.
; The memory is NOT initialized.  Written by [jrd] to allow memory to
; be allocated anywhere in the 1MB address space.
sbrk    proc    near                    ; K & R, please forgive us.
        mov     bx,ax
        add     bx,15                   ; round up
        mov     cl,4
        shr     bx,cl                   ; convert to # of paragraphs
        mov     ah,alloc                ; DOS memory allocator
        int     dos
        jc      sbrkx                   ; c = fatal
        ret                             ; and return segment in ax
sbrkx:  mov     dx,offset mfmsg         ; assume not enough memory (ax = 8).
        cmp     ax,7                    ; corrupted memory (ax = 7)?    [jrd]
        jne     sbrkx1                  ; ne = no.
        mov     dx,offset mf7msg        ; corrupted memory found. [jrd]
sbrkx1: mov     ah,prstr
        int     dos
        jmp     krmend                  ; exit Kermit now.
sbrk    endp


; Jumping to this location is like retskp.  It assumes the instruction
;   after the call is a jmp addr.

RSKP    PROC    NEAR
        pop bp
        add bp,3
        push bp
        ret

RSKP    ENDP

; Jumping here is the same as a ret.

R       PROC    NEAR
        ret
R       ENDP

code    ends                    ; End of code section.
        end     start


