NAME ccsker ; File CCSKER.ASM ;CHINESE ifdef MSDOS include mssker.dat else include ccsker.dat endif code segment public 'code' extrn cmblnk:near, locate:near, logout:near, mail:near extrn bye:near, telnet:near, finish:near, comnd:near, prompt: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, shserv:near extrn strcat:near, prtasz:near, shorx:near, lnout:near extrn scout:near,scinp:near,scpau:near,scecho:near,scclr:near extrn scxmit:near, scwait:near, srvdsa:near, srvena:near extrn shcom:near, shlog:near, shpro:near, shterm:near, shscpt:near extrn shfile:near, takopen:near, takclos:near, ask:near, assign:near extrn goto:near, screinp:near, ifcmd:near, isdev:near extrn chkos:near ; Jun. 1990 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 mcmsg erms34, cerms34 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: call chkos ; check if MS-DOS or CC-DOS ? Jun.1990 mov ah,prstr mov dx,offset machnam ; print machine name int dos mov ah,prstr ; Print the version header ; mov dx,offset versio mcmsg versio, cversio 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 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: mov comand.cmrprs,offset krmend ; address to go to on reparse mov comand.cmostp,sp ; Save for reparse too 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 mcmsg hlpmsg,chlpmsg int dos start3: call serrst ; reset serial port (if active) 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 cmp taklev,0 ; keep port open between script cmds jne kermt1 ; ne = in Take or Macro call serrst ; reset serial port for CTTY DOS use kermt1: mov dx,prmptr ; get prompt call chkos ; check if MS-DOS or CC-DOS ? Jun.1990 call prompt ; Prompt the user, set reparse address 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 mcmsgb tophlp,ctophlp cmp flags.extflg,0 ; Check if the exit flag is set jne krmend ; If so jump to KRMEND mov comand.cmcr,1 ; Allow bare CR's mov ah,cmkey mov comand.impdo,1 ; allow implied "DO macro" call comnd jmp kermt3 nop mov comand.impdo,0 ; only on initial keyword, not here mov comand.cmcr,0 ; no more bare CR's call bx ; Call the routine returned jmp kermt3 nop cmp flags.extflg,0 ; Check if the exit flag is set jne krmend ; If so jump to KRMEND jmp short kermt5 ; Do it again kermt3: mov kstatus,8 ; global status cmp flags.cxzflg,'C' ; got here via Control-C? jne kermt7 ; ne = no kermt4: cmp flags.extflg,0 ; Check if the exit flag is set jne kermt5 ; ne = yes, skip msg, do cleanup ; mov dx,offset ermes3 ; Say command not executed mcmsg ermes3,cermes3 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 call takclos ; close take file, release buffer jmp kermt5 ; close any other take files kermt7: mov flags.nmoflg,0 ; Reset filename override flag mov flags.getflg,0 ; May as well do this one cmp flags.extflg,0 ; Check if the exit flag is set jne krmend ; ne = yes, Exit now jmp kermit ; get next command krmend: call serrst ; Just in case the port wasn't reset test flags.capflg,0FFH ; Logging active? jz krmend2 ; z = no call clscpi ; close log files nop ; this skip returns.. nop nop krmend2:cmp lclexit,0 ; sys dependent routines want service? je krmend3 ; e = no mov bx,lclexit ; addr of sys dependent exit routine call bx ; call it krmend3: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 mov ah,4cH ; terminate process mov al,errlev ; return error level int dos ret START 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 nop mov flags.extflg,1 ; Set the exit flag jmp rskp ; Then return to system EXIT ENDP ; TAKE commands from a file, and allow a path name TAKE PROC NEAR mov kstatus,0 ; global status, success cmp taklev,maxtak ; Hit our limit? jl take1 ; Continue if still OK mov ah,prstr ;mov dx,offset erms30 ; Complain mcmsg erms30, cerms30 int dos ret take1: mov dx,offset tmpbuf ; work buffer mov tmpbuf,0 ; mov bx,offset filmsg ; Help in case user types "?" mcmsgb filmsg,cfilmsg mov ah,cmfile ; get file name call comnd jmp r nop mov ah,cmcfm call comnd jmp r nop mov ax,offset tmpbuf ; point to name again cmp tmpbuf,0 ; empty filespec? jne take2 ; ne = no mov ah,prstr ; mov dx,offset ermes1 ; say more parameters needed mcmsg ermes1,cermes1 int dos jmp rskp ; TAKE2: enter with ax=filename ptr TAKE2: call spath ; is it around? jc take3 ; no, go complain mov dx,ax ; point to name from spath mov ah,open2 ; 2.0 open call cmp dosnum,2 ; above DOS 2? mov al,0 ; open for reading jna take2a ; na = no, so no shared access mov al,0+40h ; open for reading, deny none take2a: int dos jnc take4 ; nc = opened ok, keep going mov ax,dx ; recover filename pointer take3: push ax mov ah,prstr ; mov dx,offset erms31 mcmsg erms31, cerms31 int dos pop ax mov dx,ax ; asciiz file name call prtasz ; display it mov kstatus,8 ; global status jmp rskp ; we've done all error displays ; TAKE4: enter with ax=filename ptr TAKE4: call takopen ; open take buffer in macro space jc take6 ; c = failure push bx mov bx,takadr ; get current frame ptr mov [bx].takhnd,ax ; save file handle mov [bx].taktyp,0feh ; mark as 2.0 file handle pop bx cmp flags.takflg,0 ; echoing Take files? je take5 ; e = no mov ah,prstr mov dx,offset crlf int dos take5: call takrd ; Get a buffer full of data take6: jmp rskp TAKE ENDP TAKRD PROC NEAR push ax push bx push cx push dx mov bx,takadr cmp [bx].taktyp,0feh ; get type of take (file?) jne takrd1 ; ne = no, do not read from disk mov dx,[bx].takbuf ; address of buffer to read into inc dx ; skip count byte in takbuf mov cx,dmasiz ; # of bytes to read push si mov si,dx ; fill buffer with spaces for takrd0: mov byte ptr [si],' ' ; Show Macro inc si loop takrd0 pop si mov cx,dmasiz push bx ; save frame address mov bx,[bx].takhnd ; file handle is stored here mov ah,readf2 ; 2.0 read call int dos pop bx ; restore frame address jnc takrd2 ; nc = successful read takrd1: mov ax,0 ; error, say zero bytes read takrd2: mov [bx].takcnt,ax ; number of bytes read mov ax,[bx].takbuf inc ax ; skip count byte in takbuf mov [bx].takptr,ax ; first new character pop dx pop cx pop bx pop ax ret TAKRD ENDP ; TAKE-QUIT Exit all Take files immediately but gracefully TAKEQIT PROC NEAR mov ah,cmcfm call comnd jmp r nop takqit1:mov ch,0 mov cl,taklev ; number of Take levels active jcxz takqit2 ; z = none call takclos ; close current Take file jmp short takqit1 ; repeat until all are closed takqit2:jmp rskp TAKEQIT ENDP ; put mskermit.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 call take2 ; Let Take do error msgs nop nop nop ret rdini1: call spath ; is it around? jc rdini2 ; no, ignore file mov dx,ax ; point to name from spath mov ah,open2 ; 2.0 open call mov al,0 ; open for reading cmp dosnum,2 ; above DOS 2? jna rdini1a ; na = no, so no shared access mov al,0+40h ; open for reading, deny none rdini1a:int dos jc rdini2 ; c = no ini file found, ignore call take4 ; use TAKE command to complete work nop ; ignore errors nop nop rdini2: ret 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 call takopen ; open take buffer in macro space mov bx,takadr mov byte ptr [bx].taktyp,0ffh ; mark as a macro mov [bx].takcnt,0 ; length of text mov di,[bx].takbuf ; offset of buffer, from takopen inc di ; skip count byte 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 ; a = 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 lea di,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 mov si,[bx].takbuf ; get address of text field inc si ; skip count byte mov cx,di ; current end pointer, (save di) sub cx,si ; current ptr minus start offset mov [bx].takcnt,cx cmp cx,0 jg gcmdl11 ; material at hand call takclos ; empty take file jmp gcmdl14 ; finish up ; 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 cmp al,',' ; comma separator? je gcmdl12 ; e = yes 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 cmp byte ptr[si],',' ; or comma separator? je gcmdl13 ; e = yes 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].takcnt,cx rep movsb ; copy it into the macro gcmdl13:mov ax,[bx].takbuf inc ax ; skip count byte mov [bx].takptr,ax ; init buffer ptr gcmdl14:pop es ret gcmdlin endp ; Enter with ax pointing to file name. Searches path for given file, ; returns with ax pointing to whole name, or carry set if file can't be found. SPATH proc near call isfile ; does it exist as it is? jc spath0 ; c = no, prepend path elements test byte ptr filtst.dta+21,10H ; subdirectory name? jnz spath0 ; nz = yes, not desired file clc ret spath0: push es ; save es around work push bx push si push di mov bx,ax ; save filename pointer in bx 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 di,es:word ptr[env] ; pick up environment segment mov es,di spath4: cmp byte ptr es:[si],0 ; end of PATH= string? je spath9 ; e = yes, exit loop mov di,offset decbuf+64 ; 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 output 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 decbuf+64 call isfile ; is it a file? jc spath4 ; c = no, keep looking test byte ptr filtst.dta+21,10H ; subdirectory name? jnz spath4 ; nz = yes pop di pop si pop bx pop es clc ret ; return success (carry clear) spath9: mov ax,bx ; restore original filename pointer pop di ; restore regs pop si pop bx pop es stc ; no file found ret spath 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 COMNT PROC NEAR ; COMMENT command mov ah,cmtxt mov bx,offset tmpbuf mov dx,0 call comnd jmp r nop jmp rskp COMNT ENDP ; change working directory cwdir proc near mov kstatus,0 ; global status mov ah,cmfile mov dx,offset tmpbuf ; mov bx,offset pthmsg mcmsgb pthmsg, cpthmsg call comnd jmp r mov ah,cmcfm call comnd jmp r cmp tmpbuf,0 ; anything given? jne cwd1 ; ne = yes mov ah,prstr ; mov dx,offset ermes1 ; say need more mcmsg ermes1,cermes1 int dos jmp rskp cwd1: mov dl,curdsk ; remember present disk mov byte ptr temp,dl mov bx,offset tmpbuf ; change of drives, if req'd cmp byte ptr [bx+1],':' ; was a drive specified? jne cwd2 ; ne = no mov dl,[bx] ; get the drive letter add bx,2 ; skip drive colon and dl,5FH ; make upper case sub dl,'A' ; convert to A = 0, etc mov ah,seldsk int dos ; change disks jc cwd3 ; c = failure inc dl ; count A = 1 internally mov curdsk,dl ; and store it cwd2: cmp byte ptr [bx],0 ; anything left? je cwd4 ; e = no mov dx,bx ; get path string mov ah,chdir ; DOS change directory int dos jnc cwd4 ; nc = success ;cwd3: mov dx,offset ermes4 ; failure cwd3: mcmsg ermes4,cermes4 mov ah,prstr int dos mov dl,byte ptr temp ; get current disk dec dl ; A = 0 for DOS mov ah,seldsk ; restore it int dos mov kstatus,8 ; global status cwd4: jmp rskp cwdir 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 kstatus,0 ; global status 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,cmfile ; filespec mov dx,di ; where to place the file specs ; mov bx,offset filmsg ; In case user wants help. mcmsgb filmsg,cfilmsg call comnd jmp r nop mov temp,ax ; save byte count mov ah,cmcfm call comnd jmp r nop cmp byte ptr temp+1,0 ; anything given? jne delet1 ; ne = yes mov ah,prstr ; mov dx,offset ermes1 ; say need something mcmsg ermes1,cermes1 int dos jmp rskp delet1: 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 mcmsg badnam, cbadnam int dos mov kstatus,8 ; global status 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 mov kstatus,0 ; global status mov ah,cmcfm call comnd jmp r mov ah,prstr mov dx,offset crlf ; start of message int dos mov dl,0 ; use current drive mov ah,36h ; get disk free space int dos cmp ax,0ffffh ; error response? je chkdsk1 ; e = yes mul bx ; sectors/cluster * clusters = sectors mov bx,dx ; save high word of sectors (> 64K) mul cx ; bytes = sectors * bytes/sector push ax ; save low word of bytes mov ax,bx ; recall sectors high word mov bx,dx ; save current bytes high word mul cx ; high word sectors * bytes/sector add ax,bx ; new high bytes + old high bytes mov dx,ax pop ax mov di,offset tmpbuf ; work space for lnout mov word ptr[di],' ' ; start with two spaces add di,2 call lnout mov si,offset spcmsg cmp isccdos,0 ; if in CCDOS ? jz chkdsk0 ; z = No mov si,offset cspcmsg chkdsk0:call strcat ; add text to end of message mov dx,offset tmpbuf call prtasz ; print asciiz string jmp rskp chkdsk1:mov ah,prstr ; mov dx,offset spcmsg2 ; say drive not ready mcmsg spcmsg2, cspcmsg2 int dos mov kstatus,8 ; global status jmp rskp CHKDSK ENDP ; Get directory listing DIRECT PROC NEAR mov kstatus,0 ; global status 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. mcmsg filmsg, cfilmsg call comnd jmp r mov byte ptr [bx],0 ; plant terminator mov si,offset tmpbuf jmp crun ; join run cmd from there DIRECT ENDP ; This is the 'HELP' command. It gives a list of the commands HELP PROC NEAR mov kstatus,0 ; global status mov ah,cmcfm call comnd ; Get a confirm jmp r mov ah,prstr ; show Quick help summary screen ; mov dx,offset qckhlp mcmsg qckhlp,cqckhlp int dos mov ah,conout mov dl,trans.escchr ; get Kermit escape character add dl,40h ; convert to printable push dx ; save it for repeats below int dos mov ah,prstr ; mov dx,offset qckhlp1 ; more help text mcmsg qckhlp1,cqckhlp1 int dos mov ah,conout pop dx push dx int dos mov ah,prstr ; mov dx,offset qckhlp2 ; more help text mcmsg qckhlp2,cqckhlp2 int dos pop dx ; recover current escape char mov ah,conout int dos mov ah,prstr ; mov dx,offset qckhlp3 ; end of help message mcmsg qckhlp3,cqckhlp3 int dos mov ah,coninq ; get a keystroke, quietly int dos cmp al,'?' ; query mark? jne helpx ; ne = no, skip second screen mov ah,prstr ; show help summary screen mov dx,offset crlf ; a few blank lines int dos int dos int dos ; mov dx,offset tophlp ; show usual cryptic help mcmsg tophlp,ctophlp int dos helpx: jmp rskp HELP ENDP ; the version command - print our version number prvers proc near mov kstatus,0 ; global status 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 mcmsg versio, cversio int dos jmp rskp prvers endp ; the show command showcmd proc near mov kstatus,0 ; global status mov ah,cmkey mov dx,offset shotab xor bx,bx ; no canned help call comnd jmp r jmp bx ; execute the handler showcmd endp ; the type command - type out a file typec proc near mov kstatus,0 ; global status 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. mcmsg filmsg, cfilmsg call comnd jmp r cmp ah,0 ; any text given? jne typec1 ; ne = yes mov ah,prstr ; mov dx,offset ermes1 ; say need more info mcmsg ermes1, cermes1 int dos jmp rskp typec1: 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 nop 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 runmsg ; In case user wants help mcmsg runmsg, crunmsg call comnd jmp r nop cmp ah,0 ; byte count jne run2 ; ne = have program name mov ah,prstr ; else complain ; mov dx,offset ermes1 ; need more info mcmsg ermes1,cermes1 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 cmp lclsusp,0 ; sys dependent routine to call je crun5 ; e = none mov bx,lclsusp ; address to call call bx ; call sys dependent suspend routine crun5: call serrst ; reset serial port (if active) 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 cmp lclrest,0 ; sys dependent routine to call je crun9 ; e = none mov bx,lclrest ; get routine's address call bx ; call sys dependent restore routine crun9: jmp rskp ; ok, return crun8: mov ah,prstr ; mov dx,offset erms37 mcmsg erms37,cerms37 int dos mov kstatus,8 ; global status jmp rskp CRUN 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 ; 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 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 mcmsg ermes2,cermes2 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) sbrkx: mcmsg mfmsg, cmfmsg cmp ax,7 ; corrupted memory (ax = 7)? jne sbrkx1 ; ne = no ; mov dx,offset mf7msg ; corrupted memory found mcmsg mf7msg, cmf7msg 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