        NAME    mssker
; File MSSKER.ASM
; Edit history:
; Last edit 7 Jan 1988
; 7 Jan 1988 Add es: seg override in parts of gcmdl procedure. [jrd]
; 1 Jan 1988 version 2.30
; 31 Dec 1987 Add DOS command line commands of "-f filespec" meaning replace
;  name mskermit.ini with filespec as the init file, and "stay" meaning
;  stay active after last command is finished.
; 21 Sept 1987 Add 'R' keyword for Receive short form. [jrd]
; 18 Aug 1987 Change CWD to CWDIR for MASM 4.5+ [jrd]
; 9 July 1987 Replace keyword CLRINP with keyword CLEAR. [jrd]
; 4 July 1987 Remove Clear and Local commands. Revise handling of Environment,
;  allow very long PATH= strings, general cleanup.[jrd]
; 7 June 1987 Add error level values when returning to DOS. [jrd]
;    0 = success, 1= send failed, 2 = receive failed, 4 = REMOTE cmd failed.
; 24 March 1987 Add Disable and Enable server commands, req by Bill Catchings
; [jrd]
; 18 March 1987 Add Path not found to isfile knowledge. [jrd]
; 1 Oct 1986 Version 2.29a
; 30 August 86 Add DOS Critical Error handler replacement and redo Control
;  Break handler to avoid aborting Kermit with the serial port interrupt
;  running and these interrupts redirected to Kermit. [jrd]
; 14 July 86 Add entry points for Scripts. [jrd]
; [2.29] code frozen on 6 May 1986 [jrd]

;****************************** Version 2.30 *****************************
; KERMIT, Celtic for "free"
;
; The name "Kermit" is a registered trade mark of Henson Associates, Inc.,
; used by permission.
;
;       Kermit-MS Program Version 2.30, 1 Jan 1988
;       Kermit-MS Program Version 2.29, 26 May 1986, plus later revisions.
;       Kermit-MS Program Version 2.27, December 6,1984
;
;       Based on the Columbia University KERMIT Protocol.
;
;       Copyright (C) 1982,1983,1984,1985,1986,1987,1988
;                                Trustees of Columbia University
;
;       Daphne Tzoar, Jeff Damens
;       Columbia University Center for Computing Activities
;       612 West 115th Street
;       New York, NY  10025
;
;       Joe R. Doupnik (version 2.29, 2.30)
;       Dept of EE, and CASS
;       Utah State University
;       Logan, Utah 84322
;
; Special thanks to Frank da Cruz, Bill Catchings, Steve Jensen, Herm Fischer,
; Vace Kundakci, and Bernie Eiben for their help and contributions.

        public  prompt, dosnum, curdsk, fpush, isfile, sbrk, crun, errlev
        public  takrd, takadr, taklev, filtst, drives, maxtry, imxtry
        public  netdone
        include mssdef.h

env     equ     2CH                     ; environment address in psp
cline   equ     80H                     ; offset in psp of command line

CSTACK  SEGMENT PARA STACK 'STACK'      ; Renamed from STACK
        dw      200 dup(0)              ; Initialize stack to all zeros.
CSTACK  ENDS

datas   segment public 'datas'
        extrn   buff:byte, comand:byte, flags:byte, pack:byte, trans:byte
        extrn   prmptr:word, inichk:byte, ttyact:byte
        extrn   machnam:byte, msfinal:byte, diskio:byte, decbuf:byte

versio  label   byte
        verdef
        db      cr,lf,'$'
hlpmsg  db      'Type ? for help',cr,lf,'$'
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 program requires DOS 2.0 or above$'
erms35  db      cr,lf,'Must specify program name$'
erms37  db      cr,lf,'Unable to execute program$'
tmsg5   db      cr,lf,'[closing log file]',cr,lf,'$'
badnam  db      cr,lf,'?Protected or no such file(s).$'
filmsg  db      ' File specification with optional path name $'
pthmsg  db      ' Name of new working directory$'

tophlp  db      cr,lf
        db      '  Bye      (to remote server)        '
        db      '  Logout   (to remote server)'
        db      cr,lf
        db      '  C or Connect  (become a terminal)  '
        db      '  Output text     (for scripts)'
        db      cr,lf
        db      '  Clear    (clear serial port buf)   '
        db      '  Pause [seconds] (for scripts)'
        db      cr,lf
        db      '  Close    (logging file)            '
        db      '  Push     (go to DOS)'
        db      cr,lf
        db      '  Comment  (text is ignored)         '
        db      '  Quit     (leave Kermit)'
        db      cr,lf
        db      '  CWD      (change dir &/or disk)    '
        db      '  R or Receive  (opt local filename)'
        db      cr,lf
        db      '  Define   (a command macro)         '
        db      '  Remote   (prefix for commands)'
        db      cr,lf
        db      '  Delete   (a file)                  '
        db      '  Run      (a program)'
        db      cr,lf
        db      '  Directory                          '
        db      '  S or Send (local file   new name)'
        db      cr,lf
        db      '  Disable  (selected server commands)'
        db      '  Server   (become a local server)'
        db      cr,lf
        db      '  Do       (a macro)                 '
        db      '  Set      (most things)'
        db      cr,lf
        db      '  Echo text   (show line on screen)  '
        db      '  Show     (various definitions)'
        db      cr,lf
        db      '  Enable   (selected server commands)'
        db      '  Space    (left on current disk)'
        db      cr,lf
        db      '  Exit     (leave Kermit)            '
        db      '  Status   (show main conditions)'
        db      cr,lf
        db      '  Finish   (to remote server)        '
        db      '  Stay     (in Kermit after startup)'
        db      cr,lf
        db      '  Get      (remote file opt new name)'
        db      '  Take     (do a command file)'
        db      cr,lf
        db      '  Hangup   (drop DTR, hangs up phone)'
        db      '  Transmit filespec [pmpt] (raw upload)'
        db      cr,lf
        db      '  Help     (show this list)          '
        db      '  Type     (a file)'
        db      cr,lf
        db      '  Input [timeout] text  (for scripts)'
        db      '  Version  (show Kermit',27H,'s id)'   ; 27H = single quote
        db      cr,lf
        db      '  Log      (Packet or Session to file)'
        db      cr,lf,'$'

comtab  db      43                      ; COMND tables
        mkeyw   'Bye',bye
        mkeyw   'C',telnet
        mkeyw   'Clear',scclr
        mkeyw   'Close',clscpt
        mkeyw   'Comment',rskp
        mkeyw   'Connect',telnet
        mkeyw   'CWD',cwdir
        mkeyw   'Define',dodef
        mkeyw   'Delete',delete
        mkeyw   'Directory',direct
        mkeyw   'Disable',srvdsa
        mkeyw   'Do',docom
        mkeyw   'Echo',scecho
        mkeyw   'Enable',srvena
        mkeyw   'Exit',exit
        mkeyw   'Finish',finish
        mkeyw   'Get',get
        mkeyw   'H',help
        mkeyw   'Hangup',dtrlow
        mkeyw   'Help',help
        mkeyw   'Input',scinp
        mkeyw   'Log',setcpt
        mkeyw   'Logout',logout
        mkeyw   'Output',scout
        mkeyw   'Pause',scpau
        mkeyw   'Push',dopush
        mkeyw   'Quit',exit
        mkeyw   'R',read
        mkeyw   'Receive',read
        mkeyw   'Remote',remote
        mkeyw   'Run',run
        mkeyw   'S',send
        mkeyw   'Send',send
        mkeyw   'Server',server
        mkeyw   'Set',setcom
        mkeyw   'Show',showcmd
        mkeyw   'Space',chkdsk
        mkeyw   'Status',status
        mkeyw   'Stay',rskp             ; this command does nothing.
        mkeyw   'Take',take
        mkeyw   'Transmit',scxmit
        mkeyw   'Type',typec
        mkeyw   'Version',prvers

shotab  db      5
        mkeyw   'Key',shokey
        mkeyw   'Macros',shomac
        mkeyw   'Modem',shomodem
        mkeyw   'Statistics',shosta
        mkeyw   'Translation',shorx

; Program storage.

ssave   dw      ?               ; Original SS when doing Command.com
in3ad   dw      0,0             ; Original break interrupt addresses
ceadr   dd      0               ; DOS Critical Error interrupt address
curdsk  db      0               ; Current disk.
origd   db      0               ; Original disk.
orgdir  db      64 dup (?)      ; original directory on original disk
drives  db      ?               ; number of disk drives on system
taklev  db      0               ; Take levels
takadr  dw      takstr-(size takinfo) ; Pointer into structure
takstr  db      (size takinfo) * maxtak dup(?)
psp     dw      ?               ; segment of Program Segment Prefix
imxtry  db      defmxtry        ; Retry limit for I packet send/rcv
maxtry  db      defmxtry        ; Retry limit for data packet send/rcv
ininm2  db      'MSKERMIT.INI',0 ; init file name for 2.0
filtst  filest  <>              ; file structure for procedure isfile
exearg  dw      ?               ; segment addr of environment (filled in below)
        dd      0               ; ptr to cmd line (filled in below)
        dw      5ch,0,6ch,0     ; our def fcb's; segment filled in later
delcmd  db      ' del ',0       ; delete command
dircmd  db      ' dir ',0       ; directory command
chkdcmd db      'chkdsk.com',0  ; space command
typcmd  db      ' type ',0      ; type command
dosnum  db      ?               ; dos version number
pthnam  db      'PATH='         ; Path environment variable
pthlen  equ     $-pthnam        ;  length of that string
pthadr  dw      0               ; offset of PATH= string

slashc  db      ' /c '          ; slashc Must directly preceed tmpbuf
tmpbuf  db      65 dup (?)      ; temp space for file names.
cmspnam db      'COMSPEC='      ; Environment variable
cmsplen equ     $-cmspnam
cmspbuf db      '\command.com',30 dup (0) ; default name plus additional space
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$'
netdone dw      0               ; network closedown call pointer
errlev  db      0               ; DOS errorlevel to be returned
datas   ends                    ; End data segment

code    segment public 'code'
        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, dtrlow:near
        extrn   clscpi:near, clscpt:near, getbaud:near
        extrn   dodef:near, setcpt:near, docom:near, shomodem:near
        extrn   server:near, lclini:near, shokey:near, shomac:near, shosta:near
        extrn   packlen:near, strlen:near, strcpy:near
        extrn   strcat:near, prtasz:near, shorx:near
        extrn   scout:near,scinp:near,scpau:near,scecho:near,scclr:near
        extrn   scxmit:near, srvdsa:near, srvena:near

        assume  cs:code, ds:datas, ss:cstack, es:nothing

START   PROC    FAR
        mov     ax,datas                ; Initialize DS.
        mov     ds,ax
        mov     psp,es                  ; remember psp address

        mov     ah,dosver
        int     dos
        mov     dosnum,al               ; remember dos version
        cmp     dosnum,2                ; earlier than DOS 2.0?
        jge     start1                  ; ge = no
        mov     ah,prstr
        mov     dx,offset erms34        ; Complain.
        int     dos
        mov     ax,psp                  ; set up exit for DOS 1
        push    ax                      ; push the segment
        mov     ax,0                    ; and the IP
        push    ax                      ; make return addr of psp:0 for DOS 1
        ret                             ; and return far to exit now
start1:
        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,setdma               ; Set disk transfer address.
        mov     dx,offset buff
        int     dos

        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     si,offset orgdir     ; place for directory path w/o drive code
        add     al,'A'-1                ; make al alphabetic disk drive again
        mov     [si],al                 ; put it into original path descriptor
        inc     si
        mov     byte ptr [si],':'       ; add drive specifier too
        inc     si
        mov     byte ptr [si],'\'       ; add root indicator as well
        inc     si
        mov     ah,gcd                  ; get current directory (path really)
        xor     dl,dl                   ; use current drive
        int     dos
        call    getpath                 ; get the path from the environment
        call    getcsp                  ; get comspec from environment
        call    memini                  ; init our memory usage
        call    lclini                  ; do local initialization

        call    getbaud                 ; Get the baud rate.
        call    dodisk                  ; See how many disk drives we have.
        call    packlen              ; Packet length in case do server comand.
        mov     ah,gswitch
        mov     al,0                    ; pick up switch character
        int     dos
        mov     slashc+1,dl
        mov     al,maxtry               ; limit # packet retries
        and     al,3fh                  ; 63 max
        mov     maxtry,al
        shl     al,1                    ; times two. I packets get more tries.
        mov     imxtry,al               ; keep that much
        add     al,maxtry               ; try three times
        js      start2                  ; s = sign bit set, too large
        mov     imxtry,al               ; imxtry = 3 * maxtry
start2: call    gcmdlin                 ; read command line
        cmp     taklev,0                ; in a Take file?
        jne     start3                  ; ne = yes, skip help msg
        mov     ah,prstr
        mov     dx,offset hlpmsg
        int     dos
start3: call    rdinit                  ; read kermit init file

; 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 time.
        and     flags.remflg,not dserver ; turn off server mode bit
        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     short kermt5            ; Do it again.

kermt2: mov     dx,offset ermes1        ; Say Unrecognized Command
        jmp     short kermt4
kermt3: mov     dx,offset ermes3        ; Say Not Confirmed.
kermt4: mov     ah,prstr                ; print the error message in dx
        int     dos
kermt5: cmp     flags.cxzflg,'C'        ; user Control-C abort?
        jne     kermt7                  ; ne = no, do normal operations
        cmp     taklev,0                ; in a Take file?
        je      kermt7                  ; e = no
        mov     bx,takadr               ; structure of current Take
        cmp     byte ptr [bx].taktyp,0ffh ; type of Take (file or macro)
        je      kermt6                  ; e = macro, do not try to close it
        mov     bx,word ptr [bx].takhnd ; Take file handle
        mov     ah,close2               ; DOS 2 file close
        int     dos                     ; close the Take file
kermt6: dec     taklev                  ; readjust Take level
        sub     takadr,size takinfo     ; readjust Take buffer
kermt7: mov     flags.nmoflg,0          ; Reset filename override flag.
        mov     flags.getflg,0          ; May as well do this one.
        jmp     kermit                  ; get next command

krmend: call    serrst                  ; Just in case the port wasn't reset
        test    flags.capflg,0FFH       ; Logging active?
        jz      krmend1                 ; z = no
        mov     dx,offset tmsg5
        mov     ah,prstr
        int     dos
        call    clscpi                  ; close log file
         nop                            ; this skip returns...
         nop
         nop
krmend1: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
        mov     ah,chdir
        int     dos
        mov     dx,offset in3ad         ; restore Control-C interrupt vector
        mov     al,23H                  ; interrupt 23H
        mov     ah,25H                  ; set interrupt vector
        int     dos                     ; ah, that's better.
        mov     dx,offset ceadr         ; DOS's Critical Error handler
        mov     al,24h                  ; interrupt 24h
        mov     ah,25h                  ; do replacement (put it back)
        int     dos
        cmp     netdone,0               ; does network cleanup pointer exist?
        je      krmend2                 ; e = no, so ignore it
        call    netdone         ; call the vector to shut network connection
krmend2:
        mov     ah,4cH                  ; terminate process
        mov     al,errlev               ; return error level
        int     dos
        ret
START   ENDP

; change working directory
cwdir   proc    near
        mov     ah,cmfile
        mov     dx,offset tmpbuf
        mov     bx,offset pthmsg
        call    comnd
         jmp    r
        mov     dx,offset tmpbuf
        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
        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
cwdir   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
        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

; Replace Int 23h and Int 24h with our own handlers.
; Revised to ask DOS for original interrupt vector contents, as suggested by
; Jack Bryans. 9 Jan 1986 jrd
; Modified again 30 August 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.
        int     dos                     ; get vector in es:bx
        mov     in3ad,bx              ; save offset address of original vector
        mov     in3ad+2,es              ;   and its segment.
        mov     al,24h                  ; DOS critical error, Int 24h
        mov     ah,35h
        int     dos
        mov     word ptr ceadr,bx       ; DOS's Critical Error handler, offset
        mov     word ptr ceadr+2,es     ;  and segment address.
        push    ds                      ; save ds around next DOS call.
        mov     ax,cs                   ; compose full address of ^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
        mov     dx,offset dosce         ; replacement Critical Error handler
        mov     al,24h                  ; interrupt 24h
        mov     ah,25h                  ; replace it
        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,cmfile
        lea     dx,[di].takbuf          ; convenient place to parse name into
        mov     bx,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
        mov     byte ptr [di].taktyp,0feh ; mark as 2.0 file handle
        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?
        je      take4                   ; e = no
        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
        jnc     takrd2                  ; nc = successful read
        mov     ax,0                    ; error, say zero bytes read

takrd2: mov     [bx].takchl,al          ; number of bytes read
        lea     ax,[bx].takbuf
        mov     [bx].takptr,ax
        pop     dx
        pop     cx
        pop     bx
        ret

TAKRD   ENDP

; Put offset of PATH= string in pthadr.
getpath proc    near
        push    bx
        push    cx
        push    dx
        mov     bx,offset pthnam        ; thing to find
        mov     cx,pthlen               ; length of it
        mov     pthadr,0                ; init offset to zero
        call    getenv                  ; get environment value
        mov     pthadr,dx
        pop     dx
        pop     cx
        pop     bx
        ret                             ; and return
getpath endp

; copy COMSPEC= environment string into cmspbuf
getcsp  proc    near
        push    bx
        push    cx
        push    dx
        push    es
        mov     bx,offset cmspnam       ; find COMSPEC=
        mov     cx,cmsplen              ; it's length
        call    getenv                  ; get environment offset in dx
        mov     di,offset cmspbuf       ; where to store string
        mov     si,dx                   ; address of COMSPEC= string
        mov     es,psp
        mov     bx,es:word ptr[env]     ; pick up environment address
        mov     es,bx
        push    ds                      ; save ds
        push    ds                      ; make ds point to environment seg
        push    es                      ; make es point to datas segment
        pop     ds
        pop     es
        cld
getcs1: lodsb                           ; get a byte from environment
        cmp     al,' '                  ; space or less?
        jg      getcs2                  ; g = no, keep copying
        mov     al,0                    ; terminate string on spaces etc
getcs2: stosb                           ; store it in cmspbuf
        or      al,al                   ; at end of string yet?
        jne     getcs1                  ; ne = no, keep copying
        pop     ds                      ; recover ds
        pop     es
        pop     dx
        pop     cx
        pop     bx
        ret                             ; and return
getcsp  endp

; Locate string variable in Environment.
; bx/ variable to find (incl =), cx/ length of variable name,
; dx/ address to store value at.
getenv  proc    near
        push    ax
        push    cx
        push    si
        push    di
        push    es
        mov     es,psp
        mov     ax,es:word ptr[env]     ; pick up environment address
        mov     es,ax
        mov     di,0                    ; start at this offset in segment
geten1: cmp     es:byte ptr [di],0      ; end of environment?
        je      geten4                  ; yes, forget it
        push    cx                      ; save counter
        push    di                      ; and offset
        mov     si,bx
        cld
        repe    cmpsb                   ; search for name
        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
geten4: mov     dx,di                   ; store offset of string
        pop     es
        pop     di
        pop     si
        pop     cx
        pop     ax
        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...
        mov     ax,offset ininm2        ; default name to try
        cmp     decbuf,0                ; alternate init file given?
        je      rdini1                  ; ne = no
        mov     ax,offset decbuf        ; yes, use it
rdini1: push    bx
        call    spath                   ; can we find it?
        pop     di
        jc      rdini6                  ; no, forget 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
        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
        mov     byte ptr [di].taktyp,0feh ; mark as a handle
        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          ; echo Take files?
        je      rdini3                  ; e = no
        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 Take macro buffer. Allow "-f filspec" to override
; normal mskermit.ini initialization filespec, allow command "stay" to
; suppress automatic exit to DOS at end of command line execution. [jrd]

gcmdlin proc    near
        mov     word ptr decbuf,0       ; storage for new init filename
        push    es
        cld
        mov     es,psp                  ; address psp
        mov     ch,0
        mov     cl,es:byte ptr[cline]   ; length of cmd line from DOS
        jcxz    gcmdl1                  ; z = empty line
        mov     si,cline+1              ; point to actual line
gcmdl0: cmp     byte ptr es:[si],' '    ; skip over leading whitespace
        ja      gcmdl2                  ; a = non-whitespace
        inc     si
        loop    gcmdl0                  ; fall through on all whitespace
gcmdl1: jmp     gcmdl14                 ; common exit jump point
gcmdl2: inc     cx                      ; include DOS's c/r
        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
        mov     [bx].takchl,0           ; chars remaining in macro
        mov     [bx].takcnt,0           ; and all chars in macro
        lea     di,[bx].takbuf          ; point at text field of take buffer
        mov     ax,ds
        mov     dx,es                   ; swap ds and es
        mov     es,ax
        mov     ds,dx                   ; ds = PSP, es = datas
gcmdl3: cmp     cx,0                    ; anything left?
        jbe     gcmdl10                 ; be = no
        lodsb                           ; get a byte
        dec     cx                      ; one less char in input string
        cmp     al,','                  ; comma?
        jne     gcmdl4                  ; no, keep going
        mov     al,cr                   ; convert to cr
        jmp     gcmdl9                  ; store it
gcmdl4: cmp     al,'-'                  ; starting a flag?
        jne     gcmdl9                  ; ne = no
        mov     ah,byte ptr[si]         ; get flag letter
        or      ah,20h                  ; convert to lower case
        cmp     ah,'f'                  ; 'f' for init file replacement?
        jne     gcmdl9                  ; ne = no
        mov     ah,byte ptr[si+1]       ; need space or tab separator
        cmp     ah,' '                  ; separator?
        ja      gcmdl9                  ; ne = no, not a flag
                                        ; strip out and analyze flag info
        inc     si                      ; point at separator
        dec     cx
gcmdl5: cmp     cx,0                    ; anything to read?
        jle     gcmdl10                 ; le = exhausted supply
        lodsb                           ; get filespec char from psp
        dec     cx                      ; one less char in source buffer
        cmp     al,' '                  ; in whitespace?
        jbe     gcmdl5                  ; be = yes, scan it off
        dec     si                      ; backup to real text
        inc     cx
                                        ; copy filspec to buffer decbuf
        push    di                      ; save current destination pointer
        mov     di,offset decbuf        ; where filespec part goes
        mov     word ptr es:[di],0      ; plant safety terminator
gcmdl6: lodsb                           ; get filespec char
        dec     cx                      ; one less available
        cmp     cx,0                    ; any chars left?
        jle     gcmdl7                  ; le = no
        cmp     al,' '                  ; in printables?
        jbe     gcmdl7                  ; be = no, all done
        cmp     al,','                  ; comma command separator?
        je      gcmdl7                  ; e = yes, all done
        stosb                           ; store filespec char
        mov     byte ptr es:[di],0      ; end filespec on a null
        jmp     short gcmdl6
gcmdl7: pop     di                      ; recover macro register
        dec     si                      ; compensate for last read above
        inc     cx
gcmdl8: cmp     cx,0                    ; strip trailing whitespace
        jbe     gcmdl10                 ; be = nothing left
        lodsb
        dec     cx
        cmp     al,' '                  ; white space?
        jbe     gcmdl8                  ; be = yes, strip it
        cmp     al,','                  ; at next command?
        je      gcmdl10                 ; e = yes, skip our own comma
        dec si                          ; back up to reread the char
        inc cx
        jmp     gcmdl3                  ; read more command text
                                        ; end of flag analysis
gcmdl9: stosb                           ; deposit most recent char
gcmdl10:cmp     cx,0                    ; anything left to read?
        jg      gcmdl3                  ; g = yes, loop
                                        ;
        mov     ax,datas                ; restore segment registers
        mov     ds,ax
        mov     es,ax                   ; return to ds=datas, es=datas
        lea     si,[bx].takbuf          ; get address of text field
        mov     cx,di                   ; current end pointer, (save di)
        sub     cx,si                   ; current ptr minus start offset
        mov     [bx].takchl,cl          ; count chars
        mov     [bx].takcnt,cx
        cmp     cx,0
        jg      gcmdl11                 ; material at hand
        dec     taklev                  ; nothing to take
        sub     bx,size takinfo         ; so reset Take info to say none
        mov     takadr,bx
        jmp     gcmdl14                 ; and exit.
                                        ; scan for command "stay"
gcmdl11:lodsb                           ; get a byte, cx and si are set above
        dec     cx
        cmp     al,' '                  ; separator?
        jbe     gcmdl12                 ; be = yes, keep looking
        mov     ah,al                   ; get first byte
        lodsb                           ; second byte after separator
        dec     cx
        or      ax,2020h                ; convert to lower case
        cmp     ax,'st'                 ; first two letters of stay
        jne     gcmdl12                 ; ne = no match
        lodsw                           ; next two letters (stay vs status)
        sub     cx,2
        or      ax,2020h                ; convert to lower case
        cmp     ax,'ya'                 ; same as our pattern?
        jne     gcmdl12                 ; ne = no match
                                        ; check for separator or end of macro
        cmp     cx,0                    ; at end of macro?
        jle     gcmdl13                 ; yes, consider current match correct
        cmp     byte ptr[si],' '        ; next char is a separator?
        jbe     gcmdl13                 ; be = yes, found correct match
gcmdl12:cmp     cx,0                    ; done yet? ("stay" not found)
        jg      gcmdl11                 ; g = not yet, look some more
        mov     si,offset eexit         ; append command "exit"
        mov     cx,leexit               ; length of string "exit"
        add     [bx].takchl,cl
        add     [bx].takcnt,cx
        rep     movsb                   ; copy it into the macro
gcmdl13:lea     ax,[bx].takbuf          ; update Take info
        mov     [bx].takptr,ax          ; init buffer ptr
        mov     [bx].takcnt+2,0         ; clear high order
gcmdl14:pop     es
        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 tmpbuf
        call    strcpy
        mov     dx,offset tmpbuf
        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                   ; where to place the file specs
        mov     dx,offset filmsg        ; 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 tmpbuf        ; 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 tmpbuf        ; del cmd
        jmp     crun                    ; join run cmd from there.
DELETE  ENDP

CHKDSK  PROC    NEAR                    ; Space command (use Chkdsk.com)
        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 tmpbuf
        call    strcpy
        mov     dx,offset tmpbuf
        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 tmpbuf
        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 tmpbuf
        call    strcpy
        mov     dx,offset tmpbuf
        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 tmpbuf
        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 tmpbuf        ; a dummy buffer
        mov     byte ptr [si],0         ; plant terminator
        jmp     short crun4             ; go run it
dopush  endp

; Run a program from within Kermit.
RUN     PROC    NEAR
        mov     ah,cmtxt                ; Get program name and any arguments.
        mov     bx,offset tmpbuf        ; place for user's text
        mov     dx,offset filmsg        ; In case user wants help.
        call    comnd
         nop
         nop
         nop
        cmp     ah,0                    ; byte count
        jne     run2                    ; ne = have program name
        mov     ah,prstr                ; else complain
        mov     dx,offset erms35
        int     dos
        jmp     rskp
run2:   mov     si,offset tmpbuf        ; source of text
        jmp     crun
RUN     ENDP

; crun - run an arbitrary program.      Rewritten by [jrd]
; Enter with ordinary asciiz command in si (such as Dir *.asm).
; Append a c/r and a null terminator and then ask command.com to do it.
CRUN    proc    near
        mov     ah,prstr           ; output crlf before executing comnd. [lba]
        mov     dx,offset crlf          ; [lba]
        int     dos                     ; display it. [lba]
        mov     di,offset tmpbuf        ; where to put full command line text
        cmp     si,di                   ; same place?
        je      crun1                   ; e = yes, don't copy ourself
        call    strcpy                  ; si holds source text
crun1:  mov     si,offset slashc        ; DOS command begins with slashc area
        mov     dx,offset slashc+1      ; si points to /c part of command line
        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     cx                      ; count the c/r
        mov     byte ptr [bx+1],0       ; and terminate
        pop     bx
        mov     [si],cl                 ; put length of argument here
crun4:  mov     exearg+2,si             ; pointer to argument string
        mov     exearg+4,ds             ; segment of same
        mov     es,psp                  ; point to psp again
        mov     exearg+8,es             ; segment of psp, use our def fcb's
        mov     exearg+12,es            ; segment of psp, ditto, for fcb 2.
        mov     ax,es:word ptr [env]    ; get environment ptr
        mov     exearg,ax               ; put into argument block
        mov     ax,ds
        mov     es,ax                   ; put es segment back
        mov     bx,offset exearg        ; es:bx points to exec parameter block
        mov     dx,offset cmspbuf       ; always use command.com
        mov     al,0                    ; load and execute...
        mov     ah,exec
        mov     ssave,sp                ; save stack ptr
        int     dos                     ; go run the program
        mov     ax,datas
        mov     ds,ax                   ; reset data segment
        mov     es,ax                   ; and extra segment
        mov     ax,cstack
        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      crun8                   ; c = error, handle.
        jmp     rskp                    ; ok, return
crun8:  mov     ah,prstr
        mov     dx,offset erms37
        int     dos
        jmp     rskp
CRUN    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

; Control Break, Interrupt 23h replacement.
; Always return with a Continue (vs Abort) condition since Kermit will cope
; with failures. [jrd]
intbrk: push ax
        push    ds
        mov     ax,datas                ; get Kermit's data segment.
        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                       ; return to caller in a Continue condition.

; Kermit's DOS Critical Error Handler, Int 24h. [jrd]
; Needed to avoid aborting Kermit with the serial port interrupt active and
; the Control Break interrupt redirected. See the DOS Tech Ref Manual for
; a start on this material; it is neither complete nor entirely accurate.
; The stack is the Kermit's stack, the data segment is unknown, interrupts
; are off, and the code segment is Kermit's. Note: some implementations of
; MS DOS may leave us in DOS's stack. Called by a DOS Int 21h function.
dosce:  test    ah,80h          ; block device (disk drive)?
        jnz     dosce1          ; nz = no; serial device, memory, etc.
        mov     al,3            ; tell DOS to Fail the Int 21h call
        iret                    ; return to DOS
dosce1: add     sp,6            ; pop IP, CS, Flags regs, from DOS's Int 24h.
        pop     ax              ; restore original callers regs existing
        pop     bx              ;  just before doing Int 21h call.
        pop     cx
        pop     dx
        pop     si
        pop     di
        pop     bp
        pop     ds
        pop     es
        mov     al,0ffh         ; signal failure (usually) the DOS 1.x way.
        push    ax              ; Kermit's IP, CS, and Flags are on the stack
        push    bp              ;  all ready for an iret, but first a word ...
        mov     bp,sp
        mov     ax,ss:[bp+8]    ; get Kermit's flags word
        or      ax,1            ; set the carry bit, signal failure DOS 2+ way
        mov     ss:[bp+8],ax    ; store new flags back in the stack.
        pop     bp              ; this avoids seeing the Interrupt flag bit
        pop     ax
        iret                    ; return to user, simulate return from Int 21h

; 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.
SPATH   proc    near
        mov     bx,ax                   ; convenient place to keep this
        call    isfile                  ; does it exist as it is?
        mov     ax,bx                   ; if so, just return original name
        jc      spath0                  ; c = nope, prepend path elements
        ret
spath0: push    es                      ; save es around work
        mov     si,ax
        mov     dl,0                    ; no '\' seen yet
        cld
spath1: lodsb
        cmp     al,2fh                  ; contains fwd slash path characters?
        je      spath1a
        cmp     al,5ch                  ; or backslash?
        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,pthadr               ; offset of PATH= string in environment
        mov     es,psp
        mov     ax,es:word ptr[env]     ; pick up environment segment
        mov     es,ax
spath4: cmp     byte ptr es:[si],0      ; end of PATH= string?
        je      spath9                  ; e = yes, exit loop
        mov     di,offset tmpbuf        ; place to put name
spath5: mov     al,byte ptr es:[si]     ; get a byte from environment string
        inc     si
        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 to null for later rereading
        jmp     short spath7            ; and break loop
spath6: mov     byte ptr [di],al        ; else stick in dest string
        inc     di
        jmp     spath5                  ; and continue
spath7: push    si                      ; save this ptr
        mov     si,bx                   ; this is user's file name
        cmp     byte ptr [di-1],2fh     ; does path end with switch char?
        je      spath8                  ; yes, don't put one in
        cmp     byte ptr [di-1],5ch     ; how about this one?
        je      spath8                  ; yes, don't put it in.
        mov     byte ptr [di],5ch       ; else add one
        inc     di
spath8: lodsb                           ; get filename character
        mov     byte ptr [di],al        ; copy filename char to tmpbuf buffer
        inc     di
        or      al,al                   ; end of string?
        jnz     spath8                  ; nz = no, copy rest of name
        pop     si                      ; restore postion in path string
        mov     ax,offset tmpbuf
        call    isfile                  ; is it a file?
        jc      spath4                  ; c = no, keep looking
        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.
; Returns status byte, fstat, with DOS status and high bit set if major error.
; Does a search-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. Revised 30 Aug 86 [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.fname,0 ; clear any old filenames
        mov     filtst.fstat,0          ; clear status byte
        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
        pushf                           ; save flags
        mov     dx,offset buff          ; reset dma
        mov     ah,setdma
        int     dos
        popf                            ; recover flags
        jnc     isfil1                  ; nc = file found
        mov     filtst.fstat,al         ; record DOS status
        cmp     al,2                    ; just "File Not Found"?
        je      isfil2                  ; e = yes
        cmp     al,3                    ; "Path not found"?
        je      isfil2                  ; e = yes
        cmp     al,18                   ; "No more files"?
        je      isfil2                  ; e = yes
        or      filtst.fstat,80h        ; set high bit for more serious error
        jmp     isfil2
isfil1: cmp     byte ptr filtst.fname,0 ; did DOS fill in a name?
        jne     isfil3                  ; nz = yes
isfil2: stc                             ; else set carry flag bit
isfil3: 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 seg
        mov     ax,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.  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                   ; bytes wanted
        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)?
        jne     sbrkx1                  ; ne = no.
        mov     dx,offset mf7msg        ; corrupted memory found
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
