knutson@ut-ngp.UUCP (Jim Knutson) (10/05/84)
This and the 6 following messages contain the source code and documentation for Kermit-MS and code for downloading the program to your micro. The source will build Kermit for the IBM-PC and any compatibles running DOS 1.x or 2.x, the NEC APC, DEC Rainbow, HP-150, Zenith Z100, and Wang. ------------------- Cut here ----------------- : Run this shell script with "sh" not "csh" PATH=:/bin:/usr/bin:/usr/ucb export PATH all=FALSE if [ $1x = -ax ]; then all=TRUE fi /bin/echo 'Extracting mscmd.asm' sed 's/^X//' <<'//go.sysin dd *' >mscmd.asm public comnd, cmcfrm, prserr, repars, cmgtch, drives, comand, fcbcpy include msdefs.h datas segment public 'datas' extrn flags:byte, trans:byte, fcb:byte, buff:byte extrn taklev:byte, takadr:word, dosnum:byte comand cmdinfo <> cmer00 db cr,lf,'?Program error Invalid COMND call$' cmer01 db cr,lf,'?Ambiguous$' cmer02 db cr,lf,'?Illegal input file spec$' cmer03 db cr,lf,'?Invalid command$' ; [19e] cmer04 db cr,lf,'?Invalid command or operand$' ; [1] cmer06 db cr,lf,'?Wildcard not allowed$' ; [21a] cmer07 db cr,lf,'?Invalid drive specificaton$' ; [21a] cmin00 db ' Confirm with carriage return$' cmin01 db ' One of the following:',cr,lf,'$' cmthlp dw 0 ; Text of help message for random input. drives db 0 ; How many drives we have. [21a] crlf db cr,lf,'$' ctcmsg db '^C$' prsp db ' $' ; Print a space. hlpmsg dw 0 ; Address of help message. spchar db 24H,26H,23H,40H,21H,25H,27H,28H,29H,2DH db 3CH,3EH,7BH,7DH,5FH,5CH,5EH,7EH,7CH,60H spclen equ $-spchar spchar2 db 24H,26H,23H,40H,21H,25H,27H,28H,29H,2DH db 7BH,7DH,5FH,5EH,7EH,60H spc2len equ $-spchar2 escspc db 10O,' ',10O,'$' ; Clear escape. clrspc db ' ',10O,'$' ; Clear space. filbuf db 60H DUP(?) ; Character buffer. tbuff db 80 DUP(?) cmdstk dw ? datas ends code segment public extrn dodel:near, ctlu:near, cmblnk:near, locate:near, takrd:near extrn clearl:near assume cs:code,ds:datas,es:datas ; This routine parses the specified function in AH. Any additional ; information is in DX and BX. ; Returns +1 on success ; +4 on failure (assumes a JMP follows the call) CMND PROC NEAR comnd: mov comand.cmstat,ah ; Save what we are presently parsing. mov cmdstk,sp ; save stack ptr locally. call cminbf ; Get chars until an action or a erase char. mov ah,comand.cmstat ; Restore 'ah' for upcoming checks. cmp ah,cmcfm ; Parse a confirm? jz cmcfrm ; Go get one. cmp ah,cmkey ; Parse a keyword? jnz cm1 jmp cmkeyw ; Try and get one. cm1: cmp ah,cmifi ; Parse an input file spec? jnz cm2 jmp cmifil ; Go get one. cm2: cmp ah,cmofi ; Output file spec? jnz cm3 jmp cmofil ; Go get one. cm3: cmp ah,cmtxt ; Parse arbitrary text. [8] jnz cm4 jmp cmtext cm4: mov ah,prstr ; Else give error. mov dx,offset cmer00 ; "?Unrecognized COMND call" int dos ret ; This routine gets a confirm. cmcfrm: call cmgtch ; Get a char. cmp ah,0 ; Is it negative (a terminator; a space or ; a tab will not be returned here as they ; will be seen as leading white space.) js cmcfr0 ret ; If not, return failure. cmcfr0: and ah,7FH ; Turn off the minus bit. cmp ah,esc ; Is it an escape? jne cmcfr2 mov ah,conout mov dl,bell ; Get a bell. int dos mov ah,0 mov comand.cmaflg,ah ; Turn off the action flag. mov bx,comand.cmcptr ; Move the pointer to before thee scape. dec bx mov comand.cmcptr,bx mov comand.cmdptr,bx dec comand.cmccnt ; Decremrnt the char count. jmp cmcfrm ; Try again. cmcfr2: cmp ah,'?' ; Curious? jne cmcfr3 mov ah,prstr ; Print something useful. mov dx,offset cmin00 int dos mov ah,prstr mov dx,offset crlf ; Print a crlf. int dos mov ah,prstr mov dx,comand.cmprmp ; Reprint the prompt. int dos mov bx,comand.cmdptr ; Get the pointer into the buffer. mov ah,'$' ; Put a $ there for printing. mov [bx],ah mov bx,comand.cmcptr dec bx ; Decrement & save the buffer pointer. mov comand.cmcptr,bx mov ah,prstr mov dx,offset comand.cmdbuf int dos mov ah,0 ; Turn off the action flag. mov comand.cmaflg,ah jmp repars ; Reparse everything. cmcfr3: cmp ah,ff ; Is it a form feed? jne cmcfr4 call cmblnk ; If so blank the screen. cmcfr4: jmp rskp ; This routine parses a keyword from the table pointed ; to in DX. The format of the table is as follows: ; ; addr: db n ; Where n is the # of entries in the table. ; db m ; M is the size of the keyword. ; db 'string$' ; Where string is the keyword. ; dw ab ; Where ab is data to be returned. ; ; The keywords must be in alphabetical order. cmkeyw: mov comand.cmhlp,bx ; Save the help. mov comand.cmptab,dx ; Save the beginning of keyword table. mov bx,dx mov ch,[bx] ; Get number of entries in table. inc bx mov dx,comand.cmdptr ; Save command pointer. mov comand.cmsptr,dx ; Save pointer's here. cmky1: cmp ch,0 ; Any commands left to check? jne cmky2 jmp cmky41 ; no, go complain cmky2: dec ch mov cl,0 ; Keep track of how many chars read in so far. call cmgtch ; Get a char. cmp ah,0 ; Do we have a terminator? jns cmky2x jmp cmky4 ; Negative number means we do. cmky2x: inc bx ; Point to first letter of keyword. inc cl ; Read in another char. mov al,[bx] cmp ah,'a' ; Less than a? jl cmky21 ; If so, don't capitalize. cmp ah,'z'+1 ; More than z? jns cmky21 and ah,137O ; Capitalize the letter. cmky21: cmp ah,al je cmky3 jg cmky2y jmp cmky41 ; Fail if ah preceeds al alphabetically. cmky2y: jmp cmky6 ; Not this keyword - try the next. cmky3: inc bx ; We match here, how 'bout next char? mov al,[bx] cmp al,'$' ; End of keyword? jne cmky3x jmp cmky7 ; Succeed. cmky3x: mov dl,al ; Save al's char here. call cmgtch inc cl ; Read in another char. mov al,dl cmp ah,'a' jl cmky31 cmp ah,'z'+1 jns cmky31 and ah,137O cmky31: cmp ah,esc+80H ; Escape Recognition (escape w/minus bit on)? je cmky3y cmp ah,'?'+80H ; A question mark? [3] je cmky3y cmp ah,' '+80H ; A space? je cmky3y cmp ah,cr+80H ; Carriage return? je cmky3y jmp cmky38 cmky3y: mov comand.cmkptr,bx ; Save bx here. mov comand.cmsiz,cx ; Save size info. mov comand.cmchr,ah ; Save char for latter. call cmambg ; See if input is ambiguous or not. jmp cmky32 ; Succeeded (not ambiguous). mov ah,comand.cmchr cmp ah,esc+80H ; Escape? je cmky3z cmp ah,'?'+80H ; maybe question mark? je cmkyj1 ; yes, go handle jmp cmky41 ; Else fail. cmky3z: mov ah,conout ; Ring a bell. mov dl,bell int dos mov bx,comand.cmcptr ; Move pointer to before the escape. dec bx mov comand.cmcptr,bx mov comand.cmdptr,bx dec comand.cmccnt ; Decrement char count. mov bx,comand.cmkptr ; Failed - pretend user never typed .... mov cx,comand.cmsiz ; ... in a char. dec cl ; Don't count the escape. dec bx mov comand.cmaflg,0 ; Reset the action flag. jmp cmky3 ; Keep checking. ; ambiguous. Print out all the keywords that match cmkyj1: mov dx,offset cmin01 mov ah,prstr int dos mov bx,comand.cmkptr ; this is current keyword mov cx,comand.cmsiz ; we are cl chars into it mov ch,0 sub bx,cx ; back up to beginning inc bx ; not counting ? mov comand.cmkptr,bx ; save beginning of kw cmkyj2: mov dl,tab ; put a tab before each keyword mov ah,conout int dos mov dx,comand.cmkptr ; get current keyword mov ah,prstr int dos ; print it mov bx,comand.cmkptr ; get keyword back dec bx mov al,[bx] ; get length mov ah,0 add ax,5 ; skip length, $, value, next length add bx,ax ; this is next keyword mov si,bx mov di,comand.cmkptr ; compare with last keyword mov comand.cmkptr,bx ; update this mov cx,comand.cmsiz dec ch ; are we at end of table? jl cmkyj3 ; yes, don't go on mov comand.cmsiz,cx ; else update count mov ch,0 dec cl ; this includes ? jcxz cmkyj2 ; empty, just print it repe cmpsb ; compare to previous string je cmkyj2 ; same, go print this one cmkyj3: jmp cmky50 ; else go finish up cmky32: mov cx,comand.cmsiz ; Restore info. mov bx,comand.cmkptr ; Our place in the keyword table. cmp comand.cmchr,0A0H ; Space? je cmky35 cmp comand.cmchr,0BFH ; Question mark? [3] je cmky35 cmp comand.cmchr,8DH ; Carriage return? je cmky35 dec comand.cmcptr ; Pointer into buffer of input. mov dx,comand.cmcptr cmky33: mov ah,[bx] ; Get next char in keyword. cmp ah,'$' ; Are we done yet? jz cmky34 mov di,dx mov [di],ah inc bx inc dx inc comand.cmccnt jmp cmky33 cmky34: mov ah,' ' mov di,dx mov [di],ah ; Put a blank in the buffer. inc dx mov cx,comand.cmcptr ; Remember where we were. mov comand.cmcptr,dx ; Update our pointers. mov comand.cmdptr,dx mov ah,'$' mov di,dx mov [di],ah ; Add '$' for printing. mov ah,prstr mov dx,cx ; Point to beginning of filled in data. int dos inc bx ; Point to address we'll need. mov bx,[bx] mov comand.cmaflg,0 ; Turn off action flag. jmp rskp cmky35: inc bx mov ah,[bx] ; Find end of keyword. cmp ah,'$' jne cmky35 inc bx mov bx,[bx] ; Address of next routine to call. ; mov comand.cmaflg,0 ; Zero the action flag. jmp rskp cmky38: cmp ah,al je cmky39 jmp cmky6 ; Go to end of keyword and try next. cmky39: jmp cmky3 ; Check next letter. cmky4: and ah,7FH ; Turn off minus bit. cmp ah,'?' ; Need help? je cmky5 cmp ah,' ' ; Just a space - no error. je cmky51 cmp ah,cr je cmky51 cmp ah,tab je cmky51 cmp ah,esc ; Ignore escape? je cmky43 cmky41: mov ah,prstr mov dx,offset cmer03 int dos jmp prserr ; Parse error - give up. cmky43: mov ah,conout ; Ring a bell. mov dl,bell int dos dec comand.cmcptr ;[ESC] don't trash BX here. dec comand.cmdptr ;[ESC] ditto dec comand.cmccnt ; Don't count the escape. mov comand.cmaflg,0 ; Reset action flag. inc ch ; Account for a previous 'dec'. jmp cmky1 ; Start over. cmky5: inc bx ; point to actual keyword mov comand.cmkptr,bx ; remember current kw mov cl,1 ; code above expects to count ? mov comand.cmsiz,cx ; and size mov dx,comand.cmhlp or dx,dx ; was any help given? jnz cmky5a ; yes, use it jmp cmkyj1 ; else make our own message cmky5a: mov ah,prstr int dos cmky50: mov ah,prstr mov dx,offset crlf int dos mov dx,comand.cmprmp ; Address of prompt. int dos mov bx,comand.cmdptr ; Get pointer into buffer. mov al,'$' mov [bx],al ; Add dollar sign for printing. mov dx,offset comand.cmdbuf int dos dec comand.cmcptr ; Don't keep it in the buffer. dec comand.cmccnt ; Don't conut it. mov comand.cmaflg,0 ; Turn off the action flag. jmp repars cmky51: cmp comand.cmcr,1 ; Are bare CR's allowed? je cmky52 ; Yes. mov ah,prstr mov dx,offset cmer04 ; Complain. int dos cmky52: jmp prserr cmky6: inc bx ; Find end of keyword. mov al,[bx] cmp al,'$' jne cmky6 inc bx ; Beginning of next command. inc bx inc bx mov dx,comand.cmsptr ; Get old cmdptr. mov comand.cmdptr,dx ; Restore. mov comand.cmsflg,0FFH jmp cmky1 ; Keep trying. cmky7: call cmgtch ; Get char. cmp ah,0 js cmky71 ; Ok if a terminator. dec bx jmp cmky6 ; No match - try next keyword. cmky71: inc bx ; Get necessary data. mov bx,[bx] cmp ah,9BH ; An escape? jne cmky72 mov ah,prstr mov dx,offset prsp ; Print a space. int dos mov di,comand.cmcptr dec di mov ah,20H mov [di],ah ; Replace escape char with space. mov comand.cmaflg,0 mov comand.cmsflg,0FFH ; Pretend they typed a space. cmky72: jmp rskp ; See if keyword is unambiguous or not from what the user has typed in. cmambg: cmp ch,0 ; Any keywords left to check? jne cmamb0 ret ; If not then not ambiguous. cmamb0: inc bx ; Go to end of keyword ... mov al,[bx] ; So we can check the next one. cmp al,'$' jne cmamb0 add bx,4 ; Point to start of next keyword. dec cl ; Don't count escape. mov dx,comand.cmsptr ; Buffer with input typed by user. cmamb1: mov ah,[bx] ; Keyword char. mov di,dx mov al,[di] ; Input char. cmp al,'a' ; Do capitalizing. jl cmam11 cmp al,'z'+1 jns cmam11 and al,137O cmam11: cmp ah,al ; Keyword bigger than input (alphabetically)? jle cmamb2 ; No - keep checking. ret ; Yes - not ambiguous. cmamb2: inc bx ; Advance one char. inc dx dec cl jnz cmamb1 jmp rskp ; Fail - it's ambiguous. cmifil: mov hlpmsg,bx ; Address of help message. mov bx,dx ; Get the fcb address in bx. mov comand.cmfcb,bx ; Save it. mov ch,0 ; Initialize char count. mov ah,0 mov [bx],ah ; Set the drive to default to current. inc bx mov comand.cmfcb2,bx mov cl,' ' cmifi0: mov [bx],cl ; Blank the FCB. inc bx inc ah cmp ah,0BH ; Twelve? jl cmifi0 cmifi1: call cmgtch ; Get another char. cmp ah,0 ; Is it an action character. js cmif1x ; Jump out of range. [21a] jmp cmifi2 ; Ditto. [21a] cmif1x: and ah,7FH ; Turn off the action bit. [21a] cmp ah,'?' ; A question mark? jne cmif12 mov al,0 mov comand.cmaflg,al ; Blank the action flag. dec comand.cmcptr ; Decrement the buffer pointer. dec comand.cmccnt ; Decrement count. mov ah,prstr mov dx,hlpmsg ; Help message. int dos mov dx,offset crlf int dos mov dx,comand.cmprmp int dos mov bx,comand.cmdptr mov al,'$' mov [bx],al ; Put in dollar sign for printing. mov dx,offset comand.cmdbuf int dos jmp repars cmif12: cmp ah,esc ; An escape? je cm12x jmp cmif13 cm12x: mov comand.cmaflg,0 ; Turn off the action flag. dec comand.cmcptr ; Move pointers to before the escape. dec comand.cmdptr dec comand.cmccnt ; Decrement char count. mov comand.cmchr,ch ; Save current character count. cmp ch,9 ; Past '.'? jl cmf120 ; No. dec ch ; Yes, don't count point. cmf120: mov di,comand.cmfcb2 ; Fill the rest with CP/M wildcards. mov ah,'?' cmf121: cmp ch,11 ; Done? jge cmf122 ; Yes. mov [di],ah inc di inc ch jmp cmf121 cmf122: mov ah,sfirst ; Find first matching file? mov dx,comand.cmfcb ;[jd] use pointer to PASSED fcb int dos cmp al,0FFH ; Any found? jne cmf123 ; Yes. jmp cmf12b ; No, lose. cmf123: mov di,offset filbuf ; Copy first file spec from DTA to buffer. mov bx,offset buff+1 mov cl,11 call fcbcpy mov di,offset filbuf+10H ; Get another copy (if not ambiguous). mov bx,offset buff+1 mov cl,11 call fcbcpy mov ah,snext ; More matching specs? mov dx,comand.cmfcb ;[jd] use PASSED fcb... int dos cmp al,0FFH je cmf124 ; Only one. mov di,offset filbuf+10H ; Copy second file spec. mov bx,offset buff+1 mov cl,11 call fcbcpy cmf124: mov si,offset filbuf ; Start comparing file names. mov bx,offset filbuf+10H mov di,comand.cmcptr ; Command buffer pointer mov cl,comand.cmchr ; Bypass characters typed. cmp cl,9 ; Past '.'? jl cmf125 ; No. dec cl ; Yes, don't count point. cmf125: mov ch,0 ; Adjust pointers. add si,cx add bx,cx mov ch,cl ; Update character count cmf126: cmp ch,11 ; All done? jne cmf127 ; No. jmp cmf12a ; Yes. cmf127: cmp ch,8 ; End of file name? jne cmf128 ; No. cmp comand.cmchr,9 ; Exactly at point? je cmf128 ; Yes, don't output a second point. mov ah,'.' ; Output separator. mov [di],ah inc di inc comand.cmccnt cmf128: mov ah,[si] ; Get a character from first file spec. inc si mov al,[bx] ; Get another from second spec. inc bx cmp ah,al ; Compare. jne cmf12a ; Ambiguous. inc ch ; Same, count. cmp ah,' ' ; Blank? je cmf129 ; Yes, don't output. mov [di],ah inc di inc comand.cmccnt cmf129: jmp cmf126 ; Repeat. cmf12a: mov comand.cmchr,ch ; Save count of characters processed. mov ah,'$' ; Put terminator into buffer. mov [di],ah mov comand.cmcptr,di ; Save pointer for recognized characters. mov ah,prstr mov dx,comand.cmdptr int dos mov ch,comand.cmchr ; Characters processed. cmp ch,11 ; Complete file name. je cmf12c ; Yes, don't beep. cmf12b: mov ah,conout ; Beep, if not recognized. mov dl,bell int dos ; Ring the bell. cmf12c: jmp repars cmif13: mov ah,ch ; It must be a terminator. cmp ah,0 ; Test the length of the file name. jnz cmf3x cmp comand.cmcr,1 ; Is zero length OK? [21a] je cmf3z ; Return successfully. [21a] jmp cmifi9 ; If zero complain. cmf3x: cmp ah,0DH js cmf3y jmp cmifi9 ; If too long complain. cmf3y: jmp rskp ; Otherwise we have succeeded. cmf3z: push es mov ax,ds mov es,ax mov di,comand.cmfcb inc di mov cx,11 mov al,'?' repne stosb pop es mov flags.wldflg,0FFH ; Remember we had a wildcard. jmp rskp cmifi2: cmp ah,'.' jne cmifi3 inc ch mov ah,ch cmp ah,1H ; Any chars yet? jnz cmf2x jmp cmifi9 ; No, give error. cmf2x: cmp ah,0AH ; Tenth char? js cmf2y jmp cmifi9 ; Past it, give an error. cmf2y: mov dl,9H mov dh,0 mov bx,comand.cmfcb add bx,dx ; Point to file type field. mov comand.cmfcb2,bx mov ch,9H ; Say we've gotten nine. jmp cmifi1 ; Get the next char. cmifi3: cmp ah,':' jne cmifi4 inc ch cmp ch,2H ; Is it in right place for a drive? je cmif3x jmp cmifi9 ; If not, complain. cmif3x: mov ch,0 ; Reset char count. mov flags.droflg,1 ; Override default drive. [21a] mov flags.nmoflg,0 ; Not so fast. [21a] mov bx,comand.cmfcb2 mov al,':' ; Use for parsing drive name. mov [bx],al dec bx ; Point to drive spec. mov si,bx push es mov ax,ds mov es,ax mov di,offset tbuff ; Borrow this buffer. mov ah,prsfcb int dos pop es cmp al,0 ; OK return code? je cmif3y ; Yes, keep going. ; mov ah,[bx] ; Get the drive name. ; sub ah,'@' ; Get the drive number. ; cmp ah,drives ; Did user specify a non-existant drive? [21a] ; jle cmif3y ; Nope, so continue. [21a] mov dx,offset cmer07 ; Fail with this error message. [21a] jmp cmif9x ; [21a] cmif3y: mov comand.cmfcb2,bx ; Put rest of filename starting here. [21a] mov ah,[bx] ; Pick up drive specified. sub ah,'@' ; Get real value. mov bx,comand.cmfcb mov [bx],ah ; Put it in the fcb. push bx mov al,' ' ; Overwrite the drive and ":". inc bx mov [bx],al inc bx mov [bx],al pop bx jmp cmifi1 cmifi4: cmp ah,'*' jne cmifi7 cmp comand.cmrflg,1 ; In receive mode? [21a] jne cmif4x ; Jump out of range. [21a] mov dx,offset cmer06 ; Set the error message. [21a] jmp cmif9x ; Fail - no wildcard allowed. [21a] cmif4x: mov ah,ch ; [21a] cmp ah,8H ; Is this in the name or type field? jns cmifi5 ; Type. mov cl,8H ; Say we have eight chars. js cmifi6 ; Name field. jmp cmifi9 ; If its where the dot should be give up. cmifi5: mov cl,0CH ; Three chars. cmifi6: mov flags.wldflg,0FFH ; Remember we had a wildcard. mov bx,comand.cmfcb2 ; Get a pointer into the FCB. mov ah,'?' mov [bx],ah ; Put a question mark in. inc bx mov comand.cmfcb2,bx inc ch mov ah,ch cmp ah,cl jl cmifi6 ; Go fill in another. jmp cmifi1 ; Get the next char. cmifi7: cmp ah,03DH ; Equals sign (wildcard)? jne cmif7x cmp comand.cmrflg,1 ; In receive mode? [21a] jne cmif7y ; No, so it's ok. [21a] mov dx,offset cmer06 ; Set the error message. [21a] jmp cmif9x ; Fail - no wildcard allowed. [21a] cmif7y: mov ah,'?' ; New label. [21a] mov flags.wldflg,0FFH ; Say we have a wildcard. jmp cmifi8 ; Put into FCB. cmif7x: cmp ah,'0' jl cmif8x cmp ah,'z'+1 jns cmif8x cmp ah,'A' ; Don't capitalize non-alphabetics. jl cmifi8 and ah,137O ; Capitalize. cmifi8: mov bx,comand.cmfcb2 ; Get the pointer into the FCB. mov [bx],ah ; Put the char there. inc bx mov comand.cmfcb2,bx mov flags.nmoflg,1 ; Overriding name from host. [21a] inc ch jmp cmifi1 cmif8x: push es mov cx,ds mov es,cx ; Scan uses ES register. mov di,offset spchar ; Special chars. mov cx,spclen ; How many of them. cmp dosnum,0 ; Under version 2.0 je cmif8y mov di,offset spchar2 mov cx,spc2len cmif8y: mov al,ah ; Char is in al. repnz scasb ; Search string for input char. cmp cx,0 ; Was it there? pop es jnz cmifi8 cmifi9: mov dx,offset cmer02 cmif9x: mov ah,prstr int dos mov flags.droflg,0 ; Not overriding drive. [21a] mov flags.nmoflg,0 ; Or name to save file under. [21a] mov comand.cmrflg,0 ; Reset this flag too. [21a] ret cmofil: jmp cmifil ; For now, the same as CMIFI. ; Parse arbitrary text up to a CR. Put chars into data buffer sent to ; the host (pointed to by BX). Called with text of help message in DX. ; Return updated pointer in BX and input size in AH. cmtext: mov comand.cmptab,bx ; Save pointer to data buffer. [8 start] mov cmthlp,dx ; Save the help message. mov cl,0 ; Init the char count. cmtxt1: mov comand.cmsflg,0 ; Get all spaces. [25] call cmgtch ; Get a char. test ah,80H ; is high-order bit on? jz cmtxt5 ; Nope, put into the buffer. and ah,07FH cmp ah,' ' je cmtxt5 cmp ah,esc ; An escape? jne cmtxt2 mov ah,conout mov dl,bell ; Ring a bell. int dos mov comand.cmaflg,0 ; Reset action flag. dec comand.cmcptr ; Move pointer to before the escape. dec comand.cmdptr dec comand.cmccnt ; Decrement count. jmp cmtxt1 ; Try again. cmtxt2: cmp ah,'?' ; Asking a question? jz cmtx30 cmp ah,ff ; Formfeed? jne cmtx2x call cmblnk cmtx2x: mov ah,cl ; Return count in AH. mov bx,comand.cmptab ; Return updated pointer. jmp rskp cmtx30: mov comand.cmaflg,0 ; Reset action flag to zero. inc comand.cmdptr ; count the ? cmp cl,0 ; Is "?" first char? jne cmtxt5 ; No, just add to buffer. dec comand.cmcptr ;[ESC] (moved 3 lines) Don't keep in buffer. dec comand.cmccnt ;[ESC] Don't conut it. dec comand.cmdptr ;[ESC] don't count if printing help. mov ah,prstr ; Else, give some help. mov dx,cmthlp ; Address of help message. int dos mov ah,prstr mov dx,offset crlf ; Print a crlf. int dos mov ah,prstr mov dx,comand.cmprmp ; Reprint the prompt. int dos mov bx,comand.cmdptr ; Get the pointer into the buffer. mov byte ptr [bx],'$' mov ah,prstr mov dx,offset comand.cmdbuf int dos jmp cmtxt1 ; And keep going. cmtxt5: inc cl ; Increment the count. mov bx,comand.cmptab ; Pointer into destination array. mov [bx],ah ; Put char into the buffer. inc bx mov comand.cmptab,bx jmp cmtxt1 ; [8 end] cmgetc: cmp taklev,0 jne cmget1 jmp cmge10 ; no take file, get from keyboard cmget1: push bx push si mov bx,takadr mov ax,[bx].takcnt or ax,[bx].takcnt+2 jnz cmget5 cmget2: mov al,byte ptr [bx].takfcb ; get first byte of fcb cmp al,0ffh ; is it really a macro? je cmget4 ; yes, better not try to close it cmp al,0feh ; or maybe a file handle? je cmget3 ; yes, close w/2.0 call mov ah,closf lea dx,[bx].takfcb int dos jmp short cmget4 ; skip over alternate close cmget3: mov bx,word ptr [bx].takfcb+1 ; this is where file handle is stored mov ah,close2 ; use 2.0 close int dos cmget4: dec taklev sub takadr,size takinfo pop si pop bx mov al,cr ; end with carriage return... ret cmget5: cmp [bx].takchl,0 ; Any chars left in buffer? jne cmget6 call takrd cmget6: dec [bx].takchl sub [bx].takcnt,1 ; DEC doesn't set carry!! sbb [bx].takcnt+2,0 mov si,[bx].takptr lodsb mov [bx].takptr,si cmp al,ctlz ; maybe control-z? je cmget2 ; yes, close take file (has to be before pops) pop si pop bx cmp al,lf ; linefeed? jne cmget7 cmp flags.takflg,0 je cmgetc ; yes, ignore it cmget7: cmp al,';' ; maybe a semicolon? je cmget9 cmp flags.takflg,0 ; Echo contents of take file? je cmget8 push dx mov dl,al mov ah,conout int dos pop dx cmget8: ret ; else just return... ; semicolon seen, ignore chars until cr cmget9: call cmgetc ; get a character? cmp al,cr ; carriage return? jne cmget9 ; no, keep reading ret ; else return it cmge10: mov ah,coninq ; Get a char. cmp flags.debug,0 ; in debug mode? je cmge11 ; yes, go on mov ah,8 ; else use read that recognizes ^C cmge11: int dos push ax ; save the char cmp al,bs ; backspace? je cmge13 ; yes, skip echo cmp al,' ' ; printable? jae cmge12 ; yes, no translation needed cmp al,cr ; this is printable je cmge12 cmp al,lf je cmge12 cmp al,tab je cmge12 mov al,' ' ; else echo a space cmge12: mov dl,al ; put char here mov ah,conout int dos ; echo it ourselves... cmge13: pop ax ; and return it cmp al,'C'-40H ; control-C? je cmge15 ; yes, go handle cmp al,';' ; semicolon? je cmget9 ; yes, ignore rest of line... cmp al,tab jne cmge14 mov al,' ' cmge14: ret cmge15: mov dx,offset ctcmsg mov ah,prstr int dos mov flags.cxzflg,'C' ; remember ^C'd mov sp,cmdstk ; restore command stack ptr ret ; and fail ; Come here is user types ^W when during input. cntrlw: mov ah,prstr mov dx,offset escspc int dos dec comand.cmccnt ; Don't include it in the count. dec comand.cmcptr ; Back up past the ^W. mov cl,comand.cmccnt mov ch,0 jcxz ctlw2 pushf push es std ; Scan backwards. mov ax,ds mov es,ax ; Point to the data area. mov di,comand.cmcptr ; Looking from here. dec di mov al,' ' repe scasb ; Look for non-space. je ctlw1 ; All spaces, nothing else to do inc di ; move back to non-space inc cx repne scasb ; look for a space jne ctlw1 ; no space, leave ptrs alone inc di inc cx ; skip back over space ctlw1: inc di mov comand.cmccnt,cl ; update count mov cx,comand.cmcptr ; remember old ptr mov comand.cmcptr,di ; update pointer sub cx,di ; this is characters moved mov al,bs ; backspace cld mov di,offset tbuff ; temporary buffer rep stosb ; put enough spaces in mov byte ptr [di],'$' ; end buffer mov dx,offset tbuff mov ah,prstr int dos ; back up cursor call clearl ; clear line pop es popf ret ; and return ctlw2: mov ah,conout mov dl,bell int dos ret cminbf: push dx push bx mov cx,dx ; Save value here too. mov ah,comand.cmaflg ; Is the action char flag set? cmp ah,0 je cminb1 jmp cminb9 ; If so get no more chars. cminb1: inc comand.cmccnt ; Increment the char count. call cmgetc mov ah,al ; Keep char in 'ah'. mov bx,comand.cmcptr ; Get the pointer into the buffer. mov [bx],ah ; Put it in the buffer. inc bx mov comand.cmcptr,bx cmp ah,'W'-64 ; Is it a ^W? jne cmnb11 call cntrlw ; Kill the previous word. jmp repars cmnb11: cmp ah,25O ; Is it a ^U? jne cminb2 cmnb12: call ctlu ; Clear out the line. mov ah,prstr mov dx,comand.cmprmp ; Print the prompt. int dos mov bx,offset comand.cmdbuf mov comand.cmcptr,bx ; Reset the point to the start. mov comand.cmccnt,0 ; Zero the count. mov dx,cx ; Preserve original value of dx. jmp repars ; Go start over. cminb2: cmp ah,bs ; Or backspace? jz cminb3 cmp ah,del ; Delete? jne cminb4 cminb3: call dodel ; Delete a character. mov ah,comand.cmccnt ; Decrement the char count by two. dec ah dec ah cmp ah,0 ; Have we gone too far? jns cmnb32 ; If not proceed. mov ah,conout ; Ring the bell. mov dl,bell int dos jmp cmnb12 ; Go reprint prompt and reparse. cmnb32: mov comand.cmccnt,ah ; Save the new char count. mov ah,prstr ; Erase the character. mov dx,offset clrspc int dos mov bx,comand.cmcptr ; Get the pointer into the buffer. dec bx ; Back up in the buffer. dec bx mov comand.cmcptr,bx jmp repars ; Go reparse everything. cminb4: cmp ah,'?' ; Is it a question mark. jz cminb6 cmp ah,esc ; Is it an escape? jz cminb8 cmp ah,cr ; Is it a carriage return? jz cminb5 cmp ah,lf ; Is it a line feed? jz cminb5 cmp ah,ff ; Is it a formfeed? jne cminb7 call cmblnk call locate cminb5: mov ah,comand.cmccnt ; Have we parsed any chars yet? cmp ah,1 jnz cminb6 jmp prserr ; If not, just start over. cminb6: mov ah,0FFH ; Set the action flag. mov comand.cmaflg,ah jmp cminb9 cminb7: jmp cminb1 ; Get another char. cminb8: mov ah,prstr ; Don't print the escape char. mov dx,offset escspc int dos jmp cminb6 cminb9: pop bx pop dx ret cmgtch: push cx push bx push dx cmgtc1: mov ah,comand.cmaflg cmp ah,0 ; Is it set. jne cmgt10 call cminbf ; If the action char flag is not set get more. cmgt10: mov bx,comand.cmdptr ; Get a pointer into the buffer. mov ah,[bx] ; Get the next char. inc bx mov comand.cmdptr,bx cmp ah,' ' ; Is it a space? jz cmgtc2 cmp ah,tab ; Or a tab? jne cmgtc3 cmgtc2: mov ah,comand.cmsflg ; Get the space flag. cmp ah,0 ; Was the last char a space? jne cmgtc1 ; Yes, get another char. mov ah,0FFH ; Set the space flag. mov comand.cmsflg,ah mov ah,' ' pop dx pop bx jmp cmgtc5 cmgtc3: mov al,0 mov comand.cmsflg,al ; Zero the space flag. pop dx pop bx cmp ah,esc jz cmgtc5 cmp ah,'?' ; Is the user curious? jz cmgtc4 cmp ah,cr jz cmgtc4 cmp ah,lf jz cmgtc4 cmp ah,ff je cmgtc4 pop cx ret ; Not an action char, just return. cmgtc4: dec comand.cmdptr cmgtc5: or ah,80H ; Make the char negative to indicate pop cx ret ; it is a terminator. CMND ENDP ; This address is jumped to on reparse. PARSE PROC NEAR repars: mov sp,comand.cmostp ; new sp <-- old sp mov bx,offset comand.cmdbuf mov comand.cmdptr,bx mov ah,0FFH mov comand.cmsflg,ah jmp comand.cmrprs ; go back to reparse address ; This address can be jumped to on a parsing error. prserr: mov sp,comand.cmostp ; Set new sp to old one. 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 taklev,0 ; in take cmd? jne prser1 ; yes, don't print prompt mov ah,prstr mov dx,offset crlf int dos mov ah,prstr ; Print the prompt. mov dx,comand.cmprmp ; Get the prompt. int dos ; Instead return to before the prompt call. prser1: jmp comand.cmrprs PARSE ENDP ; FCB must be remembered if found "*" in filename. [7 start] ; Copy from place addressed by BX to place addressed by DI. ; Also use to get the filename to the FCB from the DTA. FCBCPY PROC NEAR push es push si mov ax,ds mov es,ax ; make sure destination segment is correct mov ch,0 ; high-order part of length jcxz fcbcp1 ; zero argument (is this necessary???) mov si,bx ; this is source rep movsb ; copy the whole thing fcbcp1: pop si pop es ret ; and return FCBCPY 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 //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 640 mscmd.asm /bin/echo -n ' '; /bin/ls -ld mscmd.asm fi /bin/echo 'Extracting mscomm.asm' sed 's/^X//' <<'//go.sysin dd *' >mscomm.asm public data, spack, rpack, rpack5, portval, port1, port2, hierr include msdefs.h gettim equ 2CH ; Get the time of day. maxlp equ 100 ; Use as number of times to loop (in inchr). true equ 1 false equ 0 mntrgl equ bufsiz/4 ; Low point = 1/4 of the way full. maxpack equ 60H ; largest packet we can handle datas segment public 'datas' extrn flags:byte, trans:byte, pack:byte, count:word, xofsnt:byte port1 prtinfo <0FFFH,0,defpar,1,0,defhand,floxon> port2 prtinfo <0FFFH,0,defpar,1,0,defhand,floxon> portval dw port1 ; Default is to use port 1. hierr db 0 ; Non-ascii char (non-zero if yes). spmes db 'Spack: $' rpmes db 'Rpack: $' crlf db cr,lf,'$' infms0 db 'Waiting .....$' hibit db 'Warning - Non Ascii char$' cemsg db 'User intervention$' temp dw 0 tmp db ?,'$' pktptr dw ? ; Position in receive packet. incnt dw ? ; Number of chars read in from port. loopct db ? ; Loop counter. time dw ? ; When we should timeout. dw ? ; Want a double word. packet db ?,?,?,? ; Packet (data is part of it). data db 5AH DUP(?) ; Data and checksum field of packet. recpkt db maxpack DUP(?) ; Receive packet storage (use the following). crctab dw 00000H dw 01081H dw 02102H dw 03183H dw 04204H dw 05285H dw 06306H dw 07387H dw 08408H dw 09489H dw 0A50AH dw 0B58BH dw 0C60CH dw 0D68DH dw 0E70EH dw 0F78FH crctb2 dw 00000H dw 01189H dw 02312H dw 0329BH dw 04624H dw 057ADH dw 06536H dw 074BFH dw 08C48H dw 09DC1H dw 0AF5AH dw 0BED3H dw 0CA6CH dw 0DBE5H dw 0E97EH dw 0F8F7H datas ends code segment public extrn prtchr:near, clrbuf:near, outchr:near extrn sppos:near, stpos:near, biterr:near, intmsg:near extrn clearl:near, rppos:near, errpack:near assume cs:code, ds:datas ; Packet routines ; Send_Packet ; This routine assembles a packet from the arguments given and sends it ; to the host. ; ; Expects the following: ; AH - Type of packet (D,Y,N,S,R,E,F,Z,T) ; ARGBLK - Packet sequence number ; ARGBK1 - Number of data characters ; Returns: +1 always SPKT PROC NEAR spack: push ax ; Save the packet type. call clrbuf ; Clear the input buffer. [20e] mov bx,offset packet ; Get address of the send packet. mov ah,trans.ssoh ; Get the start of header char. mov [bx],ah ; Put in the packet. inc bx ; Point to next char. mov ax,pack.argbk1 ; Get the number of data chars. xchg ah,al mov al,trans.chklen ; Length of checksum. dec al ; Extra length of checksum. add ah,' '+3 ; Real packet character count made printable. add ah,al ; Account for checksum length in count. mov [bx],ah ; Put in the packet. inc bx ; Point to next char. mov ch,0 ; For the 16 bit checksum. mov cl,ah ; Start the checksum. mov ax,pack.argblk ; Get the packet number. add al,' ' ; Add a space so the number is printable. mov [bx],al ; Put in the packet. inc bx ; Point to next char. add cx,ax ; Add the packet number to the checksum. pop ax ; Get the packet type. mov [bx],ah ; Put in the packet. inc bx ; Point to next char. mov al,0 xchg ah,al add cx,ax ; Add the type to the checksum. mov dx,pack.argbk1 ; Get the packet size. spack2: cmp dx,0 ; Are there any chars of data? jz spack3 ; No, finish up. dec dx ; Decrement the char count. mov al,[bx] ; Get the next char. inc bx ; Point to next char. mov ah,0 add cx,ax ; Add the char to the checksum. cmp al,0 jns spack2 cmp hierr,0ffH ; Printed message already? je spack2 ; Yes, then that's it. push bx push cx push dx call biterr pop dx pop cx pop bx mov hierr,0FFH ; set err flag. jmp spack2 ; Go try again. spack3: cmp trans.chklen,2 ; What kind of checksum are we using. je spackx ; 2 characters. jg spacky ; 3 characters. mov ah,cl ; 1 char: get the character total. mov ch,cl ; Save here too (need 'cl' for shift). and ah,0C0H ; Turn off all but the two high order bits. mov cl,6 shr ah,cl ; Shift them into the low order position. mov cl,ch add ah,cl ; Add it to the old bits. and ah,3FH ; Turn off the two high order bits. (MOD 64) add ah,' ' ; Add a space so the number is printable. mov [bx],ah ; Put in the packet. inc bx ; Point to next char. jmp spackz ; Add EOL char. spacky: mov al,0 ; Get a null. mov [bx],al ; To determine end of buffer. push bx ; Don't lose our place. mov bx,offset packet+1 ; First checksummed character. call crcclc ; Calculate the CRC. pop bx push cx mov ax,cx ; Manipulate it here. and ax,0F000H ; Get 4 highest bits. mov cl,4 shr ah,cl ; Shift them over 4 bits. add ah,' ' ; Make printable. mov [bx],ah ; Add to buffer. inc bx pop cx ; Get back checksum value. spackx: push cx ; Save it for now. and cx,0FC0H ; Get bits 6-11. mov ax,cx mov cl,6 shr ax,cl ; Shift them bits over. add al,' ' ; Make printable. mov [bx],al ; Add to buffer. inc bx pop cx ; Get back the original. and cx,003FH ; Get bits 0-5. add cl,' ' ; Make printable. mov [bx],cl ; Add to buffer. inc bx spackz: mov ah,trans.seol ; Get the EOL the other host wants. mov [bx],ah ; Put in the packet. inc bx ; Point to next char. mov ah,0 ; Get a null. mov [bx],ah ; Put in the packet. cmp flags.debug,0 ; debug mode. je spack4 inc bx mov ah,'$' mov [bx],ah call sppos call clearl ; Clear to end of line. mov dx,offset crlf mov ah,prstr int dos call clearl ; Next line too. call sppos ; Reposition cursor. mov ah,prstr mov dx,offset spmes int dos mov dx,offset packet mov ah,prstr int dos ; debug end. spack4: call outpkt ; Call the system dependent routine. jmp r jmp rskp SPKT ENDP ; Write out a packet. OUTPKT PROC NEAR mov dh,trans.spad ; Get the number of padding chars. outpk2: dec dh cmp dh,0 jl outpk3 ; If none left proceed. mov ah,trans.spadch ; Get the padding char. call outchr ; Output it. jmp r ; Say we failed. [25] jmp outpk2 outpk3: mov bx,offset packet ; Point to the packet. outlup: mov ah,[bx] ; Get the next character. cmp ah,0 ; Is it a null? jnz outlp2 jmp rskp outlp2: call outchr ; Output the character. jmp r inc bx ; Increment the char pointer. jmp outlup OUTPKT ENDP ; Calculate the CRC. Returns the CRC in CX. Destroys: BX, AX. crcclc: push dx push si mov dx,0 ; Initial CRC value is 0. crc0: mov al,[bx] ; Get the first char of the string. cmp al,0 ; If null, then we're done. je crc1 inc bx xor al,dl ; Xor input with lo order byte of CRC. mov ah,al ; Get a copy. and ah,0F0H ; Get hi 4 bits. mov cl,4 shr ah,cl ; Right justify. and al,0FH ; Get lo 4 bits. push bx mov si,offset crctb2 ; Low portion of CRC factor. mov bh,0 mov bl,al add bl,al ; Get word index. mov cx,[si+bx] ; Low portion. mov si,offset crctab ; High portion of CRC factor. mov bh,0 mov bl,ah add bl,ah ; Get word index. mov bx,[si+bx] xor bx,cx ; Add the two. mov cl,8 shr dx,cl ; Shift CRC 8 bits to the right. xor dx,bx ; XOR table value and CRC. pop bx ; Retrieve index. jmp crc0 crc1: mov cx,dx ; Return it in CX. pop si pop dx ret ; Receive_Packet ; This routine waits for a packet arrive from the host. It reads ; chars until it finds a SOH. RPACK PROC NEAR rpack5: call inpkt ; Read up to a carriage return. jmp r ; Return bad. rpack0: call getchr ; Get a character. jmp r ; Hit the carriage return, return bad. cmp al,trans.rsoh ; Is the char the start of header char? jne rpack0 ; No, go until it is. rpack1: call getchr ; Get a character. jmp r ; Hit the carriage return, return bad. cmp al,trans.rsoh ; Is the char the start of header char? jz rpack1 ; Yes, then go start over. mov ch,0 ; For 16-bit checksum. mov cl,al ; Start the checksum. mov ah,0 mov pack.argbk1,ax ; Save the data count. call getchr ; Get a character. jmp r ; Hit the carriage return, return bad. cmp al,trans.rsoh ; Is the char the start of header char? jz rpack1 ; Yes, then go start over. mov ah,0 add cx,ax ; Add it to the checksum. sub al,' ' ; Get the real packet number. mov ah,0 mov pack.argblk,ax ; Save the packet number. call getchr ; Get a character. jmp r ; Hit the carriage return, return bad. cmp al,trans.rsoh ; Is the char the start of header char? jz rpack1 ; Yes, then go start over. mov ah,0 mov temp,ax ; Save the message type. [11] add cx,ax ; Add it to the checksum. ; Start of change. ; Now determine block check type for this packet. Here we violate the layered ; nature of the protocol by inspecting the packet type in order to detect when ; the two sides get out of sync. Two heuristics allow us to resync here: ; a. An S packet always has a type 1 checksum. ; b. A NAK never contains data, so its block check type is LEN-2. push cx mov cl,al mov ax,pack.argbk1 ; Get back the size. sub al,34 ; unchar(len) - 2, for SEQ & TYPE fields. mov ah,trans.chklen ; Checksum length we expect. cmp cl,'S' ; Is this an "S" packet? jne rpk0 ; Nope. mov ah,1 ; Yes, use 1 char checksum. rpk0: cmp cl,'N' ; Is this a NAK? jne rpk1 ; Nope. mov ah,al ; So, len - 2 is checksum type. rpk1: mov trans.chklen,ah ; Then, this is the chksum length. sub al,ah ; Real size of data. mov dh,al ; Need it here. mov ah,0 mov pack.argbk1,ax ; And here. pop cx ; End of change. mov bx,offset data ; Point to the data buffer. rpack2: dec dh ; Any data characters? js rpack3 ; If not go get the checksum. call getchr ; Get a character. jmp r ; Hit the carriage return, return bad. cmp al,trans.rsoh ; Is the char the start of header char? jz rpack1 ; Yes, then go start over. mov [bx],al ; Put the char into the packet. inc bx ; Point to the next character. mov ah,0 add cx,ax ; Add it to the checksum. jmp rpack2 ; Go get another. rpack3: call getchr ; Get a character. jmp r ; Hit the carriage return, return bad. cmp al,trans.rsoh ; Is the char the start of header char? jnz rpk3x jmp rpack1 ; Yes, then go start over. rpk3x: sub al,' ' ; Turn the char back into a number. cmp trans.chklen,2 ; What checksum length is in use. je rpackx ; Two character checksum. jg rpacky ; Three character CRC. mov dh,cl ; 1 char - get the character total. and dh,0C0H ; Turn off all but the two high order bits. mov ch,cl mov cl,6 shr dh,cl ; Shift them into the low order position. mov cl,ch add dh,cl ; Add it to the old bits. and dh,3FH ; Turn off the two high order bits. (MOD 64) cmp dh,al ; Are they equal? jz rpack4 ; If so finish up. jmp rpack6 ; No, we fail. rpacky: mov tmp,al ; Save value from packet here. mov ah,0 ; Three character CRC. push bx mov bx,pktptr ; Where we are in the packet. dec bx mov [bx],ah ; Add null to signify end of buffer. mov bx,offset recpkt+1 ; Where data for CRC is. call crcclc ; Calculate the CRC and put into CX. pop bx push cx mov ax,cx ; Manipulate it here. and ax,0F000H ; Get 4 highest bits. mov cl,4 shr ah,cl ; Shift them over 4 bits. pop cx ; Get back checksum value. cmp ah,tmp ; Is what we got == what we calculated? jne rpack6 call getchr ; Get next character of checsum. jmp r ; Failed. cmp al,trans.rsoh ; Restarting? jz rpack7 sub al,' ' ; Get back real value. rpackx: mov tmp,al ; Save here for now. push cx ; Two character checksum. and cx,0FC0H ; Get bits 6-11. mov ax,cx mov cl,6 shr ax,cl ; Shift them bits over. pop cx ; Get back the original. cmp al,tmp ; Are they equal? jne rpack6 ; No, we fail. call getchr ; Get last character of checsum. jmp r ; Failed. cmp al,trans.rsoh ; Restarting? jz rpack7 sub al,' ' ; Get back real value. and cx,003FH ; Get bits 0-5. cmp al,cl ; Do the last chars match? jne rpack6 rpack4: mov ah,0 mov [bx],ah ; Put a null at the end of the data. mov ax,temp ; Get the type. [11] xchg al,ah ; Packet type should be in AH. jmp rskp rpack6: ret rpack7: jmp rpack1 ; For the jump out of range. RPACK ENDP INPKT PROC NEAR mov bl,flags.cxzflg ; Remember original value. [20b] mov tmp,bl ; Store it here. [20b] inpkt1: mov bx,offset recpkt ; Point to the beginning of the packet. mov incnt,0 inpkt2: call inchr ; Get a character. jmp inpkt8 ; Return failure. [20b] nop ; Make it three bytes long. [20b] mov [bx],ah ; Put the char in the packet. inc bx inc incnt cmp ah,trans.reol ; Is it the EOL char? je inpkt3 ; ended by eol, keep going cmp incnt,maxpack ; is it too big? jbe inpkt2 ; no, keep going jmp inpkt1 ; else just start over inpkt3: cmp incnt,1 ; Ignore bare CR. [2 start] je inpkt1 mov bp,portval cmp ds:[bp].hndflg,0 ; Waiting for handshake? jz inpkt5 ; If not then proceed. inpkt4: call inchr ; Wait for the turn around char. jmp inpkt8 ; Return failure. [20b] nop ; Make it three bytes long. [20b] mov bp,portval cmp ah,ds:[bp].hands ; Is it the IBM turn around character? jne inpkt4 ; If not, go until it is. inpkt5: cmp flags.debug,0 ; In debug mode? je inpkt6 mov ah,'$' mov [bx],ah call rppos call clearl ; Clear to end of line. mov dx,offset crlf mov ah,prstr int dos call clearl ; Next line too. call rppos ; Reposition cursor. mov ah,prstr mov dx,offset rpmes int dos mov dx,offset recpkt mov ah,prstr int dos ; debug end. inpkt6: mov bx,offset recpkt mov pktptr,bx ; Save the packet pointer. mov bl,tmp ; Get the original value. [20b] cmp bl,flags.cxzflg ; Did ^X/^Z flag change? [20b] je inpkt7 ; If not, just return. [20b] cmp flags.cxzflg,'E' ; Error packet? je inpkt9 call intmsg ; Else, say we saw the interrupt. [20b] inpkt7: jmp rskp ; If so we are done. inpkt8: cmp flags.cxzflg,'C' ; Did the user type a ^C? [25] jne inpkt9 mov pack.state,'A' ret inpkt9: cmp flags.cxzflg,'E' ; How about ^E? jne inpk10 ; No just go on. mov bx,offset cemsg ; Null message for error packet. call errpack mov pack.state,'A' ret inpk10: mov bl,tmp ; Get the original value. [20b] cmp bl,flags.cxzflg ; Did ^X/^Z flag change? [20b] je inpk11 ; If not, just return failure. [20b] call intmsg ; Else, say we saw the interrupt. [20b] inpk11: jmp r INPKT ENDP inchr: cmp flags.timflg,0 ; Are timeouts turned off. je inchr1 ; Yes, so skip this stuff. cmp trans.stime,0 ; Don't time out? je inchr1 ; Yes, so skip this stuff. mov loopct,0 ; Use to check for timeout. mov ah,gettim ; Get the time. int dos mov time,cx mov time+2,dx mov ah,0 mov al,trans.stime ; Timeout when getting data. mov cl,8 shl ax,cl ; Move timeout to seconds field. add time+2,ax ; If get to this time, then timeout. jnc inchr1 inc time inchr1: call prtchr ; Is there a character to read? jmp inchr6 ; Got one. mov dl,0FFH ; To read in a char. mov ah,dconio ; Is a char on the console? int dos jz inchr2 ; If not go look for another char. mov ah,al cmp ah,cr ; Is it a carriage return? je inchr5 ; If yes, then leave. cmp ah,'Z'-100O ; Control-Z? [20b] je inchr4 ; Yes - flag it. [20b] cmp ah,'X'-100O ; Control-X? [20b] je inchr4 ; Yes - flag it. [20b] cmp ah,'E'-100O ; Control-E? je inchr4 ; Flag it and get rest of packet. cmp ah,'C'-100O ; Control-C? [25] jne inchr2 ; No, then wait for input. [25] add ah,100O ; Make it printable. [25] mov flags.cxzflg,ah ; Save it. [25] ret ; Return right away. [25] inchr2: cmp flags.timflg,0 ; Are timeouts turned off? je inchr1 ; Yes, just check for more input. cmp trans.stime,0 ; Doing time outs? je inchr1 ; No, just go check for more input. inc loopct cmp loopct,maxlp ; Times to go without checking time. jne inchr1 ; Don't check yet. mov ah,gettim ; Get the current time. int dos mov ax,time sub ax,cx ; Check hours and minutes. jl inchr5 ; Over the limit so fail. jg inchr3 ; Under the limit, keep going. mov ax,time+2 sub ax,dx ; Else, check seconds and hundreds of seconds. jle inchr5 ; Return failure. inchr3: mov loopct,0 ; Reset counter. jmp inchr1 inchr4: add ah,100O ; Make it printable. [20b] mov flags.cxzflg,ah ; Remember what we saw. [20b] jmp inchr2 ; Continue getting input. [20b] inchr5: ret inchr6: mov ah,al mov bp,portval ; Point to current port structure. cmp ds:[bp].parflg,parnon ; Is the parity none? [10] je inchr7 ; We're done. [10] and ah,7FH ; Turn off the parity bit. inchr7: cmp ds:[bp].floflg,0 ; Doing any flow control? jne inchr8 ; Yes, check it out. jmp rskp ; No, just return the data. inchr8: cmp xofsnt,true ; Have we sent flow char (XOFF)? je inchr9 ; Yes. jmp rskp ; No, just return. inchr9: cmp count,mntrgl ; Under the low trigger point? jb inchra ; Yes. jmp rskp ; No, just return. inchra: push ax mov bp,portval mov ax,ds:[bp].flowc ; Get flow control char (AH = XON, AL = XOFF). call outchr ; Send it (XON). mov xofsnt,false ; Turn off the flag. pop ax jmp rskp ; Return the character. ; Return next character in AL. GETCHR PROC NEAR push bx mov bx,pktptr ; Get the packet pointer. mov al,[bx] ; Get the char. inc bx mov pktptr,bx pop bx ; Restore BX. cmp al,trans.reol ; Is it the EOL char? jne getcr2 ; If not return retskp. ret ; If so return failure. getcr2: jmp rskp GETCHR 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 //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 640 mscomm.asm /bin/echo -n ' '; /bin/ls -ld mscomm.asm fi /bin/echo 'Extracting msdefs.h' sed 's/^X//' <<'//go.sysin dd *' >msdefs.h verdef macro db ' Kermit-MS V2.26' endm BELL EQU 07Q TAB EQU 11Q LF EQU 12Q FF EQU 14Q CR EQU 15Q XXON EQU 21Q XXOFF EQU 23Q ESC EQU 33Q DEL EQU 177Q BS EQU 08H CTLZ EQU 1AH SOH EQU 01H ; Start of header char. DOS EQU 21H CONIN EQU 01H CONOUT EQU 02H RDRIN EQU 03H PUNOUT EQU 04H LSTOUT EQU 05H DCONIO EQU 06H CONINQ EQU 07H ; quiet console input PRSTR EQU 09H CONSTAT EQU 0BH SELDSK EQU 0EH ; Select disk. [21a] OPENF EQU 0FH CLOSF EQU 10H SFIRST EQU 11H SNEXT EQU 12H DELF EQU 13H READF EQU 14H ; Read from the file. WRITEF EQU 15H MAKEF EQU 16H GCURDSK EQU 19H ; Current disk. [21a] SETDMA EQU 1AH PRSFCB equ 29H ; parse an fcb. DOSVER equ 30H ; dos version # OPEN2 EQU 3DH ; 2.0 open CLOSE2 EQU 3EH ; 2.0 close READF2 EQU 3FH ; 2.0 read. LSEEK EQU 42H ; 2.0 lseek IOCTL EQU 44H WRITEF2 EQU 40H ; 2.0 write GCD equ 47H ; 2.0 get current directory. PAREVN EQU 00H ; Even parity. [10 start] PARMRK EQU 01H ; Mark parity. PARNON EQU 02H ; No parity. PARODD EQU 03H ; Odd parity. PARSPC EQU 04H ; Space parity. CMKEY EQU 01H ; Parse a keyword. CMIFI EQU 02H ; Parse an input file spec (can be wild). CMOFI EQU 03H ; Parse an output file spec. CMCFM EQU 04H ; Parse a confirm. CMTXT EQU 05H ; Parse arbitrary text up to CR. [8] FLOXON EQU 1113H ; Use XON/XOFF for flow control. FLONON EQU 0 ; Don't do flow control. DEFHAND EQU XON ; Use XON as default handshake. DMASIZ EQU 80H ; Size of DMA. FCBSIZ EQU 25H MAXTAK EQU 05H ; Max number of TAKE's allowed. [25t] MAXTRY EQU 05Q ; Default number of retries on a packet. IMXTRY EQU 20Q ; Default number of retries send initiate. DEFESC EQU ']'-100Q ; The default escape character. DRPSIZ EQU 5EH ; Default receive packet size. DSPSIZ EQU 50H ; Default send packet size. DSTIME EQU 08H ; Default send time out interval. DRTIME EQU 0DH ; Default receive time out interval. DSRVTM EQU 30 ; Default server timeout. DSPAD EQU 00H ; Default send padding. DRPAD EQU 00H ; Default receive padding. DSPADC EQU 00H ; Default send padding char. DRPADC EQU 00H ; Default receive padding char. DSEOL EQU CR ; Default send EOL char. DREOL EQU CR ; Default receive EOL char. DSSOH EQU SOH ; Default send start-of-packet char. DRSOH EQU SOH ; Default receive start-of-packet char. DSQUOT EQU '#' ; Default send quote char. DRQUOT EQU '#' ; Default receive quote char. DQBIN EQU '&' ; Default 8-bit prefix. [21b] DRPT EQU '~' ; Default repeat prefix. DCHKLEN EQU 1 ; Default checksum length. DEFPAR EQU PARNON ; Default parity (none.) IBMPAR EQU PARMRK ; IBM's parity (mark.) [10 end] bufsiz equ 2048 ; size of serial input buffer ; baud rate definitions B00455 EQU 0 ; 45.5 baud B0050 EQU 1 ; 50 baud B0075 EQU 2 ; 75 baud B0110 EQU 3 ; 110 baud B01345 EQU 4 ; 134.5 baud B0150 EQU 5 ; 150 baud B0300 EQU 6 ; 300 baud B0600 EQU 7 ; 600 baud B1200 EQU 8 ; 1200 baud B1800 EQU 9 ; 1800 baud B2000 EQU 10 ; 2000 baud B2400 EQU 11 ; 2400 baud B4800 EQU 12 ; 4800 baud B9600 EQU 13 ; 9600 baud B19200 EQU 14 ; 19200 baud B38400 EQU 15 ; 38400 baud BAUDSIZ EQU 16 ; Number of options for baud rate. ; Structure definitions. ; Modem information. mdminfo struc mddat dw 0 ; Default to port 1. [19b start] mdstat dw 0 ; Ditto. mdcom dw 0 ; Here too. mden db 0 mddis db 0 mdmeoi db 0 mdintv dw 0 ; [19b end] mdminfo ends ; Command information. cmdinfo struc cmstat db 0 ; What is presently being parsed. cmaflg db 0 ; Non-zero when an action char has been found. cmccnt db 0 ; Non-zero if a significant char is found. cmsflg db 0 ; Non-zero when the last char was a space. cmostp dw 0 ; Old stack pointer for reparse. cmrprs dw 0 ; Address to go to on reparse. cmprmp dw 0 ; Address of prompt. cmptab dw 0 ; Address of present keyword table. cmhlp dw 0 ; Address of present help. cmdbuf db 80H DUP(0) ; Buffer for command parsing. cmfcb dw 0 ; Pointer to FCB. cmfcb2 dw 0 ; Pointer to position in FCB. cmcptr dw 0 ; Pointer for next char input. cmdptr dw 0 ; Pointer into the command buffer. cmsiz dw 0 ; Size info of user input. cmkptr dw 0 ; Pointer to keyword. cmsptr dw 0 ; Place to save a pointer. cmchr db 0 ; Save char when checking ambiguity. cmrflg db 0 ; Assume parsing filename for send. [21a] cmcr db 0 ; Say whether bare CR is allowed. cmdinfo ends ; Flags information. flginfo struc belflg db 1 ; Use bell [17a -- DT] comflg db 1 ; Use COM1 by default. [19b] abfflg db 1 ; Discard incoming file if abort. [20d] debug db 0 ; Debugging mode (default off). flwflg db 1 ; File warning flag (default on). [19c] ibmflg db 0 ; IBM flag (default off). extflg db 0 ; Exit flag (default off). vtflg db 1 ; H-19 emulation. droflg db 0 ; Override default disk drive. [21a] nmoflg db 0 ; Override name from the F packet. [21a] wldflg db 0 ; Assume no "*" in fn. [7] cxzflg db 0 ; ^X/^Z to interrupt file x-fer. [20b] xflg db 0 ; Seen "X" packet. [21c] filflg db 0 ; Non-zero when nothing in DMA buffer. eoflag db 0 ; EOF flag; non-zero on EOF. getflg db 0 ; Assume normal RECEIVE (not GET). [21a] capflg db 0 ; On if capturing data. [25] takflg db 0 ; On if echo commands of TAKE file. timflg db 0 ; Say if are timing out or not. destflg db 1 ; Incoming files destination: disk or printer. eofcz db 0 ; ^Z signals eof if non-zero. remflg db 0 ; non-zero if in remote mode. flginfo ends ; Transmission parameters trinfo struc maxdat db 0 ; Max packet size for send. chklen db 1 ; Number of characters in checksum. seol db dseol ; Send EOL char. reol db dreol ; Receive EOL char. ssoh db dssoh ; Send start-of-packet character. rsoh db drsoh ; Receive start-of-packet character. squote db dsquot ; Send quote character. rquote db drquot ; Receive quote character. spsiz db dspsiz ; Send packet size. rpsiz db drpsiz ; Receive packet size. stime db dstime ; Send timeout. (Don't timeout). rtime db drtime ; Receive timeout. spad db dspad ; Send padding. rpad db drpad ; Receive padding. spadch db dspadc ; Send padding char. rpadch db drpadc ; Receive padding char. ebquot db 'Y' ; Send 8-bit quote character. escchr db defesc ; Escape character. trinfo ends pktinfo struc pktnum dw 0 ; Packet number. numpkt dw 0 ; Total number of packets sent. numrtr dw 0 ; Total number of retries. numtry db 0 ; Number of tries on this packet. oldtry db 0 ; Number of tries on previous packet. state db 0 ; Present state of the automaton. argblk dw 0 ; For subroutine arguments. argbk1 dw 0 argbk2 dw 0 argbk3 dw 0 pktinfo ends takinfo struc takfcb db fcbsiz dup(0) takbuf db dmasiz dup(0) takptr dw 0 takchl db 0 takcnt dw 0,0 takinfo ends ; Port Information. prtinfo struc baud dw 0 ; Default baud rate. ecoflg db 0 ; Local echo flag (default off). parflg db 0 ; Parity flag (default none.) [10] floflg db 0 ; If need flow control during file x-fer. hndflg db 0 ; If need handshake during file x-fer. hands db 0 ; Default handshake. flowc dw 0 ; Do flow control with XON/XOFF. prtinfo ends mkeyw macro key,val local junk,oldval oldval equ $ db junk,key,'$' junk equ $-oldval-2 dw val endm ; definitions for terminal handler: termarg struc flgs db ? ; flags prt db ? ; port to use (0,1) cols db ? ; # columns on screen rows db ? ; # rows on screen captr dw ? ; routine to call with captured data belld dw ? ; bell divisor klen dw ? ; length of key redefinition table ktab dw ? ; address of key redefinition table krpl dw ? ; address of key replacement table escc db ? ; escape character baudb db ? ; baud rate bits. parity db ? ; parity termarg ends ; bits for flag byte scrsam equ 80h ; on if shouldn't redraw screen capt equ 40h ; capture output emheath equ 20h ; emulate heath havtt equ 10h ; have translate table trnctl equ 08h ; translate control chars modoff equ 04h ; mode line off lclecho equ 01h ; local echo //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 640 msdefs.h /bin/echo -n ' '; /bin/ls -ld msdefs.h fi /bin/echo 'Extracting msfile.asm' sed 's/^X//' <<'//go.sysin dd *' >msfile.asm public bufpnt, buff, fcb, cpfcb, chrcnt, fixfcb, init, init1, public gofil, outbuf, ptchr, gtchr, gtnfil, getfil, filbuf, public encode, decode, nulref, nulr, decbuf, errpack, rptq, public origr, rptct, rptval, clrfln, cxmsg, biterr, intmsg, public rtpos, erpos,rppos, stpos,nppos,rprpos,nrtpos,sppos, public kbpos,perpos,frpos, prtscr include msdefs.h rptmin equ 3 ; At least 3 of same char in a row. ; equates for screen positioning scrfln equ 0316H ; Place for file name. scrkb equ 0416H ; Place for percent transferred. scrper equ 0516H ; Place for Kbytes transferred. scrst equ 0616H ; Place for status. scrnp equ 0816H ; Place for number of packets. scrnrt equ 0916H ; Place for number of retries. screrr equ 0A16H ; Place for error msgs. scrhi equ 0B16H ; Err when 8th bit is on. scrfr equ 0B16H ; Rename file. scrint equ 0B16H ; Acknowledge interrupt. [20b] scrsp equ 0C00H ; Place for send packet. scrrp equ 0E00H ; Place for receive packet. scrrpr equ 1100H ; Prompt when Kermit ends. datas segment public 'datas' extrn data:byte, flags:byte, trans:byte, pack:byte, hierr:byte extrn dosnum:byte outlin db cr,lf,cr,lf db cr,lf,' File name:' db cr,lf,' KBytes transferred:' db cr,lf db cr,lf db cr,lf db cr,lf,' Number of packets:' db cr,lf,' Number of retries:' db cr,lf,' Last error: None' db cr,lf,' Last warning: None' db '$' ermes4 db 'Unable to rename file$' erms10 db '?Unable to receive data$' erms11 db '?Disk full$' erms12 db '?Unable to create file$' erms17 db 'Record length exceeds size of buffer$' infms5 db 'Renaming file to $' infms7 db 'File interrupt$' infms8 db 'File group interrupt$' hibit db 'Warning - Non Ascii char$' crlf db cr,lf,'$' printer db 0,'LPT1 ' spchar db 24H,26H,23H,40H,21H,25H,27H,28H,29H,2DH db 3CH,3EH,7BH,7DH,5FH,5CH,5EH,7EH,7CH,60H spclen equ $-spchar ; Number of special chars. spchar2 db 24H,26H,23H,40H,21H,25H,27H,28H,29H,2DH db 7BH,7DH,5FH,5EH,7EH,60H spc2len equ $-spchar2 next db 0FFH ; No next character just yet. rptval db 0 ; Repeated character. rptct db 1 ; Number of times it's repeated. rptq db drpt ; Repeat prefix. origr db drpt ; Original repeat prefix. temp1 dw ? ; Temporary storage. temp2 dw ? oloc dw 0 ; Original buffer location. [21c] osiz dw 0 ; Original buffer size. [21c] chrcnt dw ? ; Number of chars in the file buffer. outpnt dw ? ; Position in packet. bufpnt dw ? ; Position in file buffer. fdtpnt dw ? ; Pointer to within our file. fcbptr dw ? ; Position in FCB. cbfptr dw ? ; Position in character buffer. filsiz dw 0 ; Double word for filesize (in bytes.) dw 0 ofilsz dw 0 ; Original file size percent adjusted (/100). tfilsz dw 0 ; Bytes transferred. dw 0 oldper dw ? ; old percentage oldkbt dw ? ; old KB transferred. wrpmsg db ? ; non-zero if we wrote percent message percnt dw 100 ; Number to divide by for a percent. bufhex dw 80H permsg db cr,' Percent transferred:$' cxzhlp db '^X cancels file, ^Z cancels batch' db ', ^E aborts protocol' db ', ^C aborts at once' db '$' asmsg db ' AS ' asmln equ $-asmsg filbuf db 60H DUP(?) ; Character buffer. buff db dmasiz DUP(?) ; Use as our DTA. fcb db fcbsiz DUP(?) ; Use as our FCB. cpfcb db fcbsiz DUP(?) ; Save FCB in case of "*". [7] decbuf db dmasiz DUP(?) ; For decoding incoming data. datas ends code segment public extrn spack:near, cmblnk:near, locate:near, nout:near extrn putmod:near, poscur:near, clearl:near, fcbcpy:near assume cs:code,ds:datas ; Position cursor for an error message. ERPOS PROC NEAR cmp flags.xflg,1 ; Packet header seen? [21c start] jne erp0 ; No, do as normal. mov dx,offset crlf mov ah,prstr int dos ret erp0: mov dx,screrr jmp poscur ERPOS ENDP ; Position cursor for number of retries message. RTPOS PROC NEAR cmp flags.xflg,1 ; Packet header seen? [21c] jne rtp0 ; No, do as normal. ret rtp0: mov dx,scrnrt jmp poscur RTPOS ENDP ; Reassure user that we acknowledge his ^X/^Z. INTMSG PROC NEAR cmp flags.xflg,0 ; Writing to screen? jne int1 ; Yes. Don't do anything. mov dx,scrint call poscur call clearl mov dx,offset infms7 ; File interrupted? cmp flags.cxzflg,'X' ; Yes. je int0 mov dx,offset infms8 ; File group interrupted. int0: mov ah,prstr int dos int1: ret INTMSG ENDP ; Print err message that found a non-standard-Ascii char in the file. BITERR PROC NEAR cmp flags.remflg,0 ; remote mode? jne biter1 ; yes, no printing. push bx mov dx,scrhi call poscur call clearl mov ah,prstr mov dx,offset hibit int dos pop bx biter1: ret BITERR ENDP ; Clear out message about interrupted file. CXMSG PROC NEAR cmp flags.xflg,0 ; Writing to screen? jne cxm0 ; Yes. Don't do anything. mov dx,scrint call poscur call clearl cxm0: ret CXMSG ENDP ; Clear out the old filename on the screen. CLRFLN PROC NEAR mov dx,scrfln call poscur call clearl ; Clear to end of line. [19a] ret CLRFLN ENDP ; some random screen positioning functions kbpos: mov dx,scrkb ; KBytes transferred. jmp poscur perpos: mov dx,scrper ; Percent transferred. call poscur jmp clearl frpos: mov dx,scrfr ; Say renamed file. call poscur jmp clearl stpos: mov dx,scrst ; Print status of file transfer. call poscur jmp clearl nppos: mov dx,scrnp ; Number of packets sent. jmp poscur rprpos: mov dx,scrrpr ; Reprompt position. jmp poscur nrtpos: mov dx,scrnrt ; Number of retries. jmp poscur sppos: mov dx,scrsp ; Send packet location. jmp poscur rppos: mov dx,scrrp ; Receive packet location. jmp poscur ; Initialize buffers and clear line. INIT PROC NEAR call cmblnk call locate mov ah,prstr ; Put statistics headers on the screen. mov dx,offset outlin int dos mov dx,offset cxzhlp call putmod ; write mode line mov wrpmsg,0 ; haven't printed the messsage yet. call init1 ret INIT ENDP INIT1 PROC NEAR mov chrcnt,dmasiz ; Number of chars left. mov bufpnt,offset buff ; Addr for beginning. mov hierr,0 ret INIT1 ENDP ; Output the chars in a packet. ; Called with AX = size of the data, BX = address of source. FILEIO PROC NEAR ptchr: mov cx,ax lea ax,outbuf ; Where to put data when buffer gets full. jmp decode ; CX = Size of data, BX = Address of data, AX = Routine to call to ; dump data. decode: push si push di push es push dx push ax mov ax,ds mov es,ax pop ax mov si,bx ; Source of data. mov bx,ax ; Coroutine to call. mov di,bufpnt ; Destination of data. mov dh,0 ; assume no quote char cmp trans.ebquot,'N' ; no quoting? je decod1 ; yes, keep going cmp trans.ebquot,'Y' ; or not doing it? je decod1 ; yes, keep going mov dh,trans.ebquot ; otherwise use quote char decod1: mov rptct,0 ; Reset. mov rptval,0 ; Ditto. dec cx jge dcod11 ; More data. jmp decod6 ; Else, we're through. dcod11: dec chrcnt ; Decrement number of chars in dta. jns decod2 ; Continue if space left. push cx push dx push bx call bx ; Output it if full. jmp decod5 ; Error return if disk is full. nop pop bx pop dx pop cx mov di,bufpnt decod2: cmp rptct,0 ; Doing a repeat? je dcod20 ; No, so go get a character. mov ah,0 mov al,rptval ; Get the character we're repeating. jmp decod4 ; And write it out to the file. dcod20: lodsb ; Pick up a char. cmp rptq,0 ; Doing repeat quoting? je dcod21 ; Nope, skip this part. cmp al,rptq ; Did we pick up the repeat quote char? jne dcod21 ; No, continue processing it. lodsb ; Get the size. dec cx ; Modify buffer count. sub al,20H ; Was made printable. mov rptct,al ; Remember how many repetitions. lodsb ; Get the char to repeat. dec cx ; Modify buffer count. dcod21: mov ah,00H ; Assume no 8-bit quote char. [21b start] cmp al,dh ; This the 8-bit quot char? jne decod3 lodsb ; Get the real character. dec cx ; Decrement # chars in packet mov ah,80H ; Turn on 8-bit quot char flag. [21b end] decod3: cmp al,trans.squote ; Is it the quote char? [21b] [21c] jne decod4 ; If not proceed. lodsb ; Get the quoted character dec cx ; Decrement # of chars in packet. or ah,al ; save parity (combine with prefix) and ah,80h ; only parity and al,7FH ; Turn off the parity bit. cmp al,trans.squote ; Is it the quote char? [21c] je decod4 ; If so just go write it out. cmp al,dh ; This the 8-bit quot char? je decod4 ; If so, just go write it out cmp al,rptq ; Is is the repeat quote character? je decod4 ; If so, just write it out. add al,40H ; Make it a control char again. and al,7FH ; Modulo 128. decod4: or al,ah ; or in parity stosb ; store the character dec rptct ; Repeat counter. cmp rptct,0 ; Write out char again? jg dcod41 jmp decod1 ; No, get next char. dcod41: mov rptval,al ; Save the char. jmp dcod11 ; and loop to next char. decod5: pop bx pop dx ; dx is pushed twice (really) pop cx pop dx pop es pop di pop si ret decod6: mov bufpnt,di pop dx pop es pop di pop si jmp rskp ; Return successfully if done. ; output the buffer, reset bufpnt and chrcnt outbuf: cmp flags.xflg,1 ; Writing to screen? [21c] je outbf2 ; Yes, handle specially. [21c] push bx mov ah,writef ; The write code. mov dx,offset fcb int dos ; Write the record. pop bx cmp al,0 ; Successful. jz outbf1 push ax ; Remember the return code. [20d] call abfil ; Fix things up before aborting. [20d] pop ax ; Retrive return code. [20d] cmp al,01 jz outbf0 call erpos mov ah,prstr mov dx,offset erms17 ; Record length exceeds dta. int dos ret outbf0: call erpos mov ah,prstr ; Tell about it. mov dx,offset erms11 ; Disk full error. int dos ret outbf1: add tfilsz+2,80H ; Say 128 more characters received. adc tfilsz,0 call kbpr ; Print the kilobytes received. call perpr ; Print the percent ('?' for now). outb11: mov bufpnt,offset buff ; Addr for beginning. mov chrcnt,dmasiz-1 ; Buffer size. jmp rskp outbf2: mov cx,dmasiz-1 ; Number of chars to write. [21c] sub cx,chrcnt ; minus # of unused in buffer mov di,offset buff ; Where they are. [21c] call prtscr ; Output buffer to screen. [21c] jmp outb11 ; Reset counter & pointer. [21c] ; Tidy up before aborting. [20d] ABFIL PROC NEAR mov ah,closf ; Close the file. mov dx,offset fcb int dos cmp flags.abfflg,1 ; Delete what got across or keep it? jne abfil0 ; Nope, keep it. mov ah,delf ; Delete it. mov dx,offset fcb int dos abfil0: mov bx,offset erms10 ; Text of message to send. call errpack ; Send an error packet. ret ABFIL ENDP ; General routine for sending an error packet. Register BX should ; point to the text of the message being sent in the packet. [20f] ERRPACK PROC NEAR mov di,offset data ; Where to put the message. mov al,0 errp1: mov ah,[bx] cmp ah,'$' ; At end of message? je errp2 inc al ; Remember number of chars in msg. mov [di],ah inc bx inc di jmp errp1 errp2: mov ah,0 mov pack.argbk1,ax mov ah,'E' ; And send an error packet. call spack ret ; Return if succeed or fail. nop nop ret ERRPACK ENDP ; Get the chars from the file. gtchr: cmp flags.filflg,0 ; Is there anything in the DMA? jz gtchr0 ; Yup, proceed. mov ah,rptq mov origr,ah ; Save repeat prefix here. mov rptct,1 ; Number of times char is repeated. mov rptval,0 ; Value of repeated char. call inbuf jmp gtchr1 ; No more chars, go return EOF. nop ; Make three bytes long. gtchr0: lea bx,inbuf jmp encode gtchr1: mov ax,0ffffh ret ; encode - writes data portion of kermit packet into filbuf. ; expects BX to contain the address of a routine to refill the buffer, ; chrcnt to be the # of chars in the buffer, trans.maxdat to contain ; the maximum size of the data packet, bufpnt to contain a pointer to ; the source of the characters. ; Returns: AX/ the number of characters actually written to the buffer. encode: mov cl,trans.maxdat ; Maximum packet size. [21b] mov ch,0 mov di,offset filbuf ; Where to put the data. mov si,bufpnt ; pointer into source buffer mov dl,trans.rquote ; send quote char mov dh,0 ; assume no 8-bit quoting cmp trans.ebquot,'N' ; not doing 8-bit quoting je encod1 cmp trans.ebquot,'Y' ; or can but won't? je encod1 mov dh,0ffh ; remember we have to do it encod1: dec cx ; Decrement output buffer counter. jge encod2 ; Go on if there is more than one left. sub di,offset filbuf mov ax,di mov bufpnt,si ; update pointer into DMA. jmp rskp encod2: dec chrcnt ; any data in buffer? jge encod3 ; yes, skip over buffer refill. call bx ; Get another buffer full. jmp encod8 mov si,bufpnt ; update position in DMA. cmp chrcnt,0 ; no characters returned? jne encod3 ; Got some, keep going. jmp encod8 ; none, assume eof. encod3: lodsb cmp rptq,0 ; Are we doing repeat prefixing? je encd3x ; Nope, skip next part. cmp chrcnt,0 ; Are we on the last character? jle encd31 ; Yes, so there's no next character. cmp rptct,94 ; Max number that we can put in a byte. je encd31 ; Then that's it. mov ah,[si] ; Get the next character. cmp al,ah ; Is current char == next char? jne encd31 inc rptct ; Number of times char appears. mov rptval,al ; Remember the character. inc cx ; Repeats don't take up so much buffer space. jmp encod1 ; Keep checking for more. encd31: cmp rptct,1 ; Were previous characters repeats? je encd3x ; No, so just add this char. cmp rptct,rptmin ; Are we within bounds for repeat prefixing? jge encd32 ; Yes, use repeat prefixing. mov al,rptct mov ah,0 sub si,ax ; Not enough characters to warrant it. mov rptval,0 ; Clear out this value. inc cx ; Adjust output buffer pointer. mov al,rptq mov origr,al ; Save original repeat prefix. mov rptq,0 ; Pretend we're not doing the prefixing. mov al,rptct mov ah,0 add chrcnt,ax ; Adjust input buffer pointer. jmp encod1 ; Reprocess those characters. encd32: push ax ; Do repeat prefixing - save data. mov al,rptq ; Add repeat prefix char. stosb dec cx ; Account for it in buffer size. mov al,rptct ; Get the repeat count. add al,20H ; Make it printable. stosb ; Add to buffer. dec cx pop ax ; Get back the actual character. mov rptct,1 ; Reset repeat count. mov rptval,0 ; And this. encd3x: cmp dh,0 ; are we doing 8-bit quoting? je encod4 ; no, forget this. test al,80h ; parity on? je encod4 ; no, don't bother with this and al,7fh ; turn off parity push ax ; save original char for a bit dec cx ; decrement # of chars left mov al,trans.ebquot ; get quote char stosb ; save in buffer pop ax ; restore character encod4: mov ah,al ; save character and ah,80h ; only parity and al,7fh ; turn off parity in character cmp al,' ' ; Compare to a space. jl encod5 ; If less then its a control char. cmp al,del ; Is the char a delete? jz encod5 ; Go quote it. cmp al,dl ; Is it the quote char? je encod6 ; Yes - go add it. [21b start] cmp dh,0 ; are we doing 8-bit quoting? je encd41 ; no, don't translate it cmp al,trans.ebquot ; Is it the 8-bit quote char? je encod6 ; Yes, just output with quote encd41: cmp origr,0 ; Doing repeat prefixing? je encod7 ; No, don't check for quote char. cmp al,origr ; Is this the repeat quote character. je encod6 ; Yes, then quote it. jmp short encod7 ; else don't quote it. encod5: add al,40h ; control char, uncontrollify and al,7fh encod6: push ax ; save the char dec cx mov al,dl stosb pop ax encod7: or al,ah ; put parity back stosb cmp rptct,1 ; One occurence of this char? jne encd7x mov al,origr mov rptq,al ; Restore repeat quote char. jmp encod1 ; Yes, so loop around for some more. encd7x: dec rptct ; Add another entry of this char. jmp encod1 ; With quoting and all. encod8: sub di,offset filbuf or di,di je encod9 ; Nope. mov ax,di jmp rskp encod9: mov ax,0FFFFH ; Get a minus one. ret inbuf: mov ah,flags.eoflag ; Have we reached the end? cmp ah,0 jz inbuf0 ret ; Return if set. inbuf0: push si push di push dx push bx push cx mov bx,offset buff ; Set the r/w buffer pointer. mov bufpnt,bx mov ah,readf ; Read a record. mov dx,offset fcb int dos mov cx,filsiz cmp cx,0 ; Check for 128 chars or less left. jne inbuf1 ; Still have data left. mov ax,ds mov es,ax mov si,offset filsiz+2 mov di,offset bufhex cmps filsiz+2,es:bufhex ja inbuf1 ; More than 128 chars. mov flags.eoflag,0FFH ; Set End-of-file. mov cx,filsiz+2 cmp flags.filflg,0 ; Ever used DMA? [25] jnz inbf01 dec cx ; Account for DEC in caller routine. inbf01: mov chrcnt,cx ; Return proper number of chars. mov flags.filflg,0 ; Buffer not empty. pop cx pop bx pop dx pop di pop si jmp rskp inbuf1: sub filsiz+2,80H ; Sent another 128 chars. sbb filsiz,0 ; Account for the doubleword. add tfilsz+2,80H ; Book keeping for the same. adc tfilsz,0 push ax call kbpr ; Print the kilobytes sent. call perpr ; Print the percent sent. pop ax mov al,80H ; Use as counter for number of chars read. pop cx pop bx pop dx pop di pop si cmp flags.filflg,0 ; Ever used DMA? jnz inbf21 ; Nope, then don't change count. dec al ; Fix boundary error. inbf21: mov ah,0 ; Zero the flag (buffer not empty). mov chrcnt,ax ; Number of chars read from file. mov flags.filflg,0 ; Buffer not empty. jmp rskp nulref: mov chrcnt,0 ; No data to return. jmp rskp nulr: ret ; Print the number of Kilobytes transferred. kbpr: cmp flags.remflg,0 ; remote mode? jne kbpr1 ; yes, no printing. mov ax,tfilsz+2 mov bx,tfilsz mov cl,10 shr ax,cl ; divide by 1024 mov cl,6 ; high order moves 16-10 = 6 bits shl bx,cl or ax,bx cmp ax,oldkbt ; is it the same? je kbpr1 ; yes, skip printing mov oldkbt,ax ; save new # of kb push ax call kbpos ; Postion the cursor. pop ax call nout ; Print the number of KBytes transferred. kbpr1: ret ; Print the percent transferred. perpr: cmp flags.remflg,0 ; remote mode? jne perpr5 ; yes, no printing. mov ax,tfilsz or ax,tfilsz+2 cmp ax,oldper ; same as it was before? je perpr5 ; yes, don't bother printing. mov oldper,ax ; remember this for next time cmp ofilsz,0 ; No divide by zeroes. je perpr5 ; If not proceed. cmp wrpmsg,0 ; did we write the percentage message? jne perpr1 ; yes, skip this part call perpos ; position cursor mov dx,offset permsg mov ah,prstr int dos ; write out message mov wrpmsg,1 ; init flag so we don't do it again perpr1: call perpos ; Position the cursor. perpr2: mov dx,tfilsz ; Get the high order word. mov ax,tfilsz+2 ; Get the low order word. div ofilsz ; Div by percent adjusted original file size. cmp ax,100 ; > 100% ? jle perpr3 ; no, accept it mov ax,100 ; else just use 100 perpr3: call nout mov dl,'%' ; Load a percent sign. perpr4: mov ah,conout ; Print the character. int dos perpr5: ret getfil: mov ah,0FFH mov flags.filflg,ah ; Nothing in the DMA. mov ax,0 mov flags.eoflag,ah ; Not the end of file. mov bx,offset fcb+0CH mov [bx],ax ; Zero the current block number. mov bx,offset fcb+0EH mov [bx],ax ; Ditto for Lrecl. mov bx,offset fcb+20H mov [bx],ah ; Zero the current record (of block). inc bx mov [bx],ax ; Same for record (of file). mov bx,offset fcb+23H mov [bx],ax mov ah,openf ; Open the file. mov dx,offset fcb int dos mov dx,word ptr fcb+18 ; get file size (hi order word) mov filsiz,dx mov ax,word ptr fcb+16 ; lo order word mov filsiz+2,ax div percnt ; Divide by 100. mov ofilsz,ax mov tfilsz,0 ; Set bytes sent to zero. mov tfilsz+2,0 mov oldkbt,-1 mov oldper,-1 cmp filsiz,0 ; Null file? jne getfl0 ; Nope. cmp filsiz+2,0 ; Null file? jne getfl0 ; Nope. mov flags.eoflag,0FFH ; Set EOF. getfl0: jmp rskp gtnfil: cmp flags.cxzflg,'Z' ; Did we have a ^Z? [20b] je gtn5 ; If yes, we're done sending files. [20b] cmp flags.wldflg,0 ; Was there a "*"? [7 start] je gtn5 ; Nope. mov bx,offset cpfcb ; Get FCB from last check for file. mov di,offset fcb ; Copy to FCB. mov cl,37 ; Size of FCB. call fcbcpy gtn2: mov ah,snext mov dx,offset fcb ; More files? int dos cmp al,0FFH je gtn5 mov bx,offset fcb mov di,offset cpfcb mov cl,37 call fcbcpy ; Copy from FCB. mov di,offset fcb+1 ; Get name of next file to send. mov bx,offset buff+1 mov cl,11 call fcbcpy call getfil ; Initialize jmp r jmp rskp gtn5: mov flags.wldflg,0 ; Reset wild card flag. ret ; [7 end] ; Get the file name (including host to micro translation) gofil: cmp flags.xflg,1 ; Remote command? [21c] jne goflx ; No.... [21c] jmp gofla ; Yes so skip this stuff. [21c] goflx: cmp flags.nmoflg,1 ; Overriding name from other side? [21a] jne gofil0 ; No - get the filename. [21a] jmp gofil7 ; Yes, so ignore packet contents. [21a] gofil0: mov bx,offset data ; Get the address of the file name. [21a] mov fdtpnt,bx ; Store the address. mov bx,offset fcb+1 ; Address of the FCB. mov fcbptr,bx ; Save it. mov ax,0 mov temp1,ax ; Initialize the char count. mov temp2,ax cmp flags.droflg,1 ; Default drive? [21a] je gofil1 ; No - don't blank out value in FCB. [21a] mov si,offset fcb mov [si],ah ; Set the drive to default to current. gofil1: mov ch,' ' ; Moved the label. [21a] mov [bx],ch ; Blank the FCB. inc bx inc ah cmp ah,0BH ; Twelve? jl gofil1 gofil2: mov bx,fdtpnt ; Get the NAME field. mov ah,[bx] inc bx mov fdtpnt,bx cmp ah,'.' ; Seperator? jne gofil3 mov bx,offset fcb+9H mov fcbptr,bx mov ax,temp1 mov temp2,ax mov temp1,9H jmp gofil6 gofil3: cmp ah,0 ; Trailing null? jz gofil7 ; Then we're done. call verlet ; Verify that the char is legal. mov bx,fcbptr mov [bx],ah inc bx mov fcbptr,bx mov ax,temp1 ; Get the char count. inc ax mov temp1,ax cmp ax,8H ; Are we finished with this field? jl gofil2 gofil4: mov temp2,ax mov bx,fdtpnt mov ah,[bx] inc bx mov fdtpnt,bx cmp ah,0 jz gofil7 cmp ah,'.' ; Is this the terminator? jne gofil4 ; Go until we find it. gofil6: mov bx,fdtpnt ; Get the TYPE field. mov ah,[bx] inc bx mov fdtpnt,bx cmp ah,0 ; Trailing null? jz gofil7 ; Then we're done. call verlet ; Verify that the char is legal. mov bx,fcbptr mov [bx],ah inc bx mov fcbptr,bx inc temp1 ; Increment char count. cmp temp1,0CH ; Are we finished with this field? jl gofil6 gofil7: cmp flags.remflg,0 ; remote mode? jne gofil7a ; yes, don't print it. call prtfn ; Print the file name. [21a] gofil7a:cmp flags.destflg,0 ; Writing to the printer? jne gf7y push es mov ax,ds mov es,ax ; Set this up. mov cx,11 mov si,offset printer mov di,offset fcb repne movsb ; Change name in FCB to be printer. pop es jmp gofil9 gf7y: mov ah,flags.flwflg ; Is file warning on? cmp ah,0 jnz gf7x jmp gofil9 ; If not, just proceed. gf7x: mov ah,openf ; See if the file exists. mov dx,offset fcb int dos cmp al,0FFH ; Does it exist? jnz gf8x jmp gofil9 ; If not create it. gf8x: cmp flags.remflg,0 ; remote mode? jne gf8xa ; yes, skip printing call frpos ; Position cursor. mov ah,prstr ; Inform the user we are renaming the file. mov dx,offset infms5 int dos gf8xa: mov ax,temp2 ; Get the number of chars in the file name. cmp ax,0 jne gofil8 mov ax,temp1 mov temp2,ax gofil8: mov ch,0 mov cl,al mov al,0 ; Says if first field is full. cmp cl,9H ; Is the first field full? jne gofl81 mov al,0FFH ; Set a flag saying so. dec cl gofl81: mov bx,offset fcb ; Get the FCB. add bx,cx ; Add in the character number. mov ah,'&' mov [bx],ah ; Replace the char with an ampersand. push ax push bx mov ah,openf ; See if the file exists. mov dx,offset fcb int dos pop bx cmp al,0FFH ; Does it exist? pop ax jz gofl89 ; If not create it. cmp al,0 ; Get the flag. jz gofl83 dec cl ; Decrement the number of chars. cmp cl,0 jz gofl88 ; If no more, die. jmp gofl81 gofl83: inc cl ; Increment the number of chars. cmp cl,9H ; Are we to the end? jl gofl81 ; If not try again ; else fail. gofl88: cmp flags.remflg,0 ; remote mode? jne gofl88a ; yes, no printing call erpos ; Position cursor. mov ah,prstr ; Tell the user that we can't rename it. mov dx,offset ermes4 int dos gofl88a:mov bx,dx ; Tell host can't rename. [20f] call errpack ; Send error packet before abort. [20f] ret gofl89: cmp flags.remflg,0 ; remote mode jne gofil9 ; yes, don't have to print it mov bx,offset fcb+0CH ; Point past the end of the file name. mov dh,[bx] ; Save the present contents. mov ah,'$' mov [bx],ah ; Put in a dollar sign. push dx mov ah,prstr ; Print the file name. mov dx,offset fcb+1 int dos pop dx mov bx,offset fcb+0CH ; Restore over the dollar sign. mov [bx],dh gofil9: mov ah,delf ; Delete the file if it exists. mov dx,offset fcb int dos mov ax,0 mov si,offset fcb+0CH mov [si],ax ; Zero current block. mov si,offset fcb+0EH mov [si],ax ; Same for Lrecl. mov si,offset fcb+20H mov [si],ah ; Zero the current record (within block). inc si mov [si],ax ; Zero record (within file). mov si,offset fcb+23H mov [si],ax mov ofilsz,0 ; File size unknown. mov tfilsz,0 ; Set bytes received to zero. mov tfilsz+2,0 mov oldkbt,-1 mov oldper,-1 mov ah,makef ; Now create it. mov dx,offset fcb int dos cmp al,0FFH ; Is the disk full? je gf9x jmp rskp gf9x: cmp flags.remflg,0 ; remote mode? jne gf9xa ; yes, don't try printing call erpos ; Position cursor. mov ah,prstr ; If so tell the user. mov dx,offset erms12 int dos mov bx,dx gf9xa: call errpack ; Send an error packet. ret gofla: cmp pack.argbk1,0 ; Any data in "X" packet? [21c start] je gofla1 ; Nothing to print. mov ah,prstr mov dx,offset crlf int dos mov di,offset data ; Where data is. mov cx,pack.argbk1 ; How much data we have. call prtscr ; Print it on the screen. gofla1: mov ah,prstr mov dx,offset crlf int dos jmp rskp ; And done. [21c end] FILEIO ENDP ; Passed char of incoming filename in AH. Verify that it is legal ; and if not change it to an "X". verlet: cmp ah,'0' jl ver2 ; See if it's a legal weird char. cmp ah,'z'+1 jns ver2 cmp ah,'9' jle ver1 ; It's between 0-9 so it's OK. cmp ah,'A' jl ver2 ; Coud be a weird char. cmp ah,'Z' jle ver1 ; It's A-Z so it's OK. cmp ah,'a' jl ver2 and ah,137O ; It's a-z, capitalize. ver1: ret ver2: push es mov cx,ds mov es,cx ; Scan uses ES register. mov di,offset spchar ; Special chars. mov cx,spclen ; How many of them. cmp dosnum,0 ; Under version 2.0 je ver3 mov di,offset spchar2 mov cx,spc2len ver3: mov al,ah ; Char is in al. repnz scasb ; Search string for input char. pop es mov ah,al ; Return it in AH. cmp cx,0 ; Was it there? jnz ver1 ; Yes, return it. mov ah,'X' ; If illegal, replace with "X". mov flags.nmoflg,1 ret ; Print incoming filename(s). [21a] PRTFN PROC NEAR call clrfln ; Position cursor & blank out the line. mov di,offset data ; Where to put the name. mov bx,offset fcb ; Where it is now. cmp flags.droflg,0 ; Drive specified? je prtfn1 mov dl,[bx] ; Which one did they say? add dl,'@' ; Make it readable. mov ah,dconio ; Print the drive name. int dos mov dl,':' int dos prtfn1: inc bx ; Point to start of filename. cmp flags.nmoflg,0 ; Is filename in packet? je prtfn2 ; no, keep going add di,pack.argbk1 ; bump by length of remote name mov si,offset asmsg ; something to put after it mov cx,asmln ; length of it rep movsb ; add this to the buffer prtfn2: mov cx,8 ; At most 8 letters in file name. mov si,bx ; this is source now prtfn3: lodsb ; get a letter cmp al,' ' ; Done with name? je prtfn4 ; yes, continue stosb ; else store loop prtfn3 ; and loop thru rest prtfn4: mov si,offset fcb+9 ; Point to file type. cmp byte ptr [si],' ' ; is there a type? je prtfn5 ; Nope so we're done. mov al,'.' ; Add the dot. stosb mov cx,3 ; At most 3 letters in file type. rep movsb ; copy type (incl trailing spaces) prtfn5: mov byte ptr [di],'$' ; end the string mov ah,prstr ; Print the file name. mov dx,offset data int dos mov flags.droflg,0 ; Reset flag once have the full name. mov flags.nmoflg,0 ret PRTFN ENDP ; Print data onto the screen. If text has no "$" in it, just print ; it. Else, do special output for the "$". ; Routine expects: DI = Start of buffer we are to print. ; CX = Number of characters to print. [21c] PRTSCR PROC NEAR mov al,'$' ; This is what we're looking for. mov oloc,di ; Remember original buffer address. mov osiz,cx ; And original size. push es mov bx,ds mov es,bx ; Have ES point to data area. prts0: repnz scasb ; Search for "$" in the buffer. cmp cx,0 ; Found one? je prts1 ; No, do a regular DOS call. mov ah,prstr mov dx,oloc ; Print up to the "$". int dos mov ah,dconio mov dl,'$' int dos ; Print the "$" mov oloc,di ; New starting location. mov osiz,cx ; New size. jmp prts0 prts1: mov bx,oloc ; The buffer location. add bx,osiz ; Point past the data. mov [bx],al ; Add "$" for printing. mov ah,prstr mov dx,oloc int dos pop es ret PRTSCR ENDP FIXFCB PROC NEAR push ax ; Don't forget this. [22] mov bx,offset fcb+18 mov di,offset filsiz mov ax,[bx] mov [di],ax mov bx,offset fcb+16 mov ax,[bx] mov 2[di],ax pop ax ; Get number of chars in last buffer full. [22] sub filsiz+2,ax ; Get real file size. sbb filsiz,0 mov bx,offset fcb+18 mov di,offset filsiz mov ax,[di] mov [bx],ax mov bx,offset fcb+16 mov ax,2[di] mov [bx],ax ret FIXFCB 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 //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 640 msfile.asm /bin/echo -n ' '; /bin/ls -ld msfile.asm fi
knutson@ut-ngp.UUCP (Jim Knutson) (10/05/84)
: Run this shell script with "sh" not "csh"
PATH=:/bin:/usr/bin:/usr/ucb
export PATH
all=FALSE
if [ $1x = -ax ]; then
all=TRUE
fi
/bin/echo 'Extracting mskerm.asm'
sed 's/^X//' <<'//go.sysin dd *' >mskerm.asm
public prompt, dosnum, curdsk, swchar
include msdefs.h
;******************** Version 2.26 **********************************
; KERMIT, Celtic for "free"
;
; The name "Kermit" is a registered trade mark of Henson Associates, Inc.,
; used by permission.
;
; Kermit-MS Program Version 2.26, July 26, 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
setblk equ 4AH
exec equ 4BH
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 20 ; Bytes for file name and size.
maxnam equ 10
chmod equ 43H ; chmod call (used to test for file existence)
STACK SEGMENT PARA STACK 'STACK'
DW 100 DUP(0) ; Initialize stack to all zeros.
STK EQU THIS WORD
STACK ENDS
datas segment public 'datas'
extrn buff:byte, comand:byte, flags:byte, pack:byte, trans:byte
extrn fcb:byte, cpfcb:byte, prmptr:word, inichk:byte
extrn machnam:byte
public takadr,taklev
versio label byte
verdef
db cr,lf
db 'Type ? for help',cr,lf
db '$'
tmp db ?,'$'
crlf db cr,lf,'$'
ermes1 db cr,lf,'?Unrecognized command$'
ermes3 db cr,lf,'?Not confirmed$'
erms30 db cr,lf,'Passed maximum nesting level for TAKE command$'
erms31 db cr,lf,'Take file not found$'
erms32 db cr,lf,'File(s) not found$'
erms33 db cr,lf,'CHKDSK program not found on current disk$'
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$'
infms1 db 'Really erase *.*? $'
infms8 db cr,lf,'File(s) erased$'
tmsg5 db cr,lf,'[closing log file]',cr,lf,'$' ; [jd]
filhlp1 db ' Command file specification $'
filhlp2 db ' File specification (possibly wild) $'
filhlp3 db ' File spec (possibly wild) or confirm with carriage return$'
filmsg db ' File specification with optional path name $'
filwmsg db ' File specification (possibly wild) with optional path name $'
chkfil db 0,'CHKDSK COM'
chkflen equ $-chkfil
tophlp db cr,lf
db 'BYE',tab,tab
db 'CLOSE',tab,tab
db 'CONNECT',tab,tab
db 'DEFINE',tab,tab
db cr,lf
db 'DELETE',tab,tab
db 'DIRECTORY',tab
db 'DO',tab,tab
db 'EXIT',tab,tab
db cr,lf
db 'FINISH',tab,tab
db 'GET',tab,tab
db 'HELP',tab,tab
db 'LOCAL',tab,tab
db cr,lf
db 'LOG',tab,tab
db 'LOGOUT',tab,tab
db 'PUSH',tab,tab
db 'QUIT',tab,tab
db cr,lf
db 'RECEIVE',tab,tab
db 'REMOTE',tab,tab
db 'RUN',tab,tab
db 'SEND',tab,tab
db cr,lf
db 'SERVER',tab,tab
db 'SET',tab,tab
db 'SHOW',tab,tab
db 'SPACE',tab,tab
db cr,lf
db 'STATUS',tab,tab
db 'TAKE'
db '$'
lochlp 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 command interpreter'
db '$'
; COMND tables
yestab db 2
mkeyw 'NO',0
mkeyw 'YES',1
comtab db 27
mkeyw 'BYE',bye
mkeyw 'C',telnet
mkeyw 'CLOSE',clscpt
mkeyw 'CONNECT',telnet
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
loctab db 5
mkeyw 'DELETE',delete
mkeyw 'DIRECTORY',direct
mkeyw 'PUSH',dopush
mkeyw 'RUN',run
mkeyw 'SPACE',chkdsk
shotab db 2
mkeyw 'KEY',shokey
mkeyw 'MACRO',shomac
; Program storage.
oldstk dw ? ; Storage for system stack.
oldsts dw ? ; System stack segment.
ssave dw ? ; Original SS when doing CHKDSK.
siz dw ? ; Memory size.
in3ad dd 0 ; Original break interrupt addresses. [25]
curdsk db 0 ; Current disk.
origd db 0 ; Original disk.
fildat db 0 ; Manipulate file data/time creation.
db 0
taklev db 0 ; Take levels. [25t]
takadr dw takstr-(size takinfo) ; Pointer into structure. [25t]
temp dw 0
temp1 dw ? ; Temporary storage.
temp2 dw ?
temp3 dw ?
temp4 dw ?
psp dw ?
divst dw 0
takstr db (size takinfo) * maxtak dup(?)
ininam db 0,'MSKERMITINI' ; init file name, on default disk, 12 chars
ininm2 db 'MSKERMIT.INI',0 ; init file name for 2.0
nambuf db maxnam * namsiz dup (?)
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
dircmd db ' /c dir '
dirclen equ $-dircmd
dirnam db 50h dup (?)
chkdcmd db 'chkdsk.com'
chkdlen equ $-chkdcmd
dosnum db ? ; dos version number
pthnam db 'PATH='
pthlen equ $-pthnam
pthbuf db 100 dup (?) ; buffer for path definition.
defpth db '\', 70 dup (?) ; buffer for default path
cmspnam db 'COMSPEC='
cmsplen equ $-cmspnam
cmspbuf db '\command.com',0 ; default name
db 30 dup (?) ; some additional space
tfile db 100 dup (?) ; temp space for file names.
eexit db cr,'exit',cr
leexit equ $-eexit
swchar db '\' ; default switch character.
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
assume cs:code,ds:datas,ss:stack,es:nothing
push ds ; Save system data area.
sub ax,ax ; Get a zero.
push ax ; Put zero return addr on stack.
mov ax,datas ; Initialize DS.
mov ds,ax
sub ax,ax
mov oldstk,sp ; Save old stack pointer.
mov ax,es:[2] ; In program segment prefix
mov siz,ax ; Pick up memory size
mov psp,es
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 getbaud ; Get the baud rate. [25]
call dodisk ; See how many disk drives we have. [21a]
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 ah,dosver
int dos
mov dosnum,al ; remember dos version
cmp al,0
je start1 ; 1.1, keep going
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
start1: 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.
; 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.droflg,0 ; Reset drive override flag.
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 sp,oldstk
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
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.
SETINT PROC NEAR
push ds ; Don't forget this. [25]
mov ax,ds
mov es,ax ; So can access our data area.
mov ax,0
mov ds,ax ; Access low core.
mov ax,ds:[23H * 4] ; Address for interrupt 23H.
mov cx,ds:[23H * 4 +2] ; CS value for it.
mov word ptr es:in3ad,ax ; Remember original values.
mov word ptr es:in3ad+2,cx
mov ax,cs
mov ds,ax ; Access code are.
mov dx,offset intbrk
mov al,23H ; On ^C, goto above address.
mov ah,25H
int dos
pop ds
ret
SETINT ENDP
; take commands from a file, but allow a path name
PTAKE PROC NEAR
cmp taklev,maxtak ; Hit our limit?
jl ptake1 ; Continue if still OK.
mov ah,prstr
mov dx,offset erms30 ; Complain.
int dos
ret
ptake1: 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
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 ptake2 ; 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 ptake3 ; open ok, keep going
ptake2: mov ah,prstr
mov dx,offset erms31
int dos
ret
ptake3: inc taklev
mov takadr,di
mov word ptr [di].takfcb+1,ax ; save file descriptor
mov byte ptr [di].takfcb,0feh ; mark as 2.0 file descriptor
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
je ptake4
mov ah,prstr
mov dx,offset crlf
int dos
ptake4: call takrd ; Get a buffer full of data.
jmp rskp
PTAKE ENDP
; TAKE commands from a file. [25t]
TAKE PROC NEAR
cmp dosnum,0
je take1
jmp ptake ; use this for 2.0
take1: cmp taklev,maxtak ; Hit our limit?
jl take2 ; Continue if still OK.
mov ah,prstr
mov dx,offset erms30 ; Complain.
int dos
ret
take2: mov bx,takadr
add bx,size takinfo
push bx
lea dx,[bx].takfcb
mov comand.cmcr,0 ; Filename must be specified.
mov ah,cmifi
mov bx,offset filhlp1
call comnd
pop bx
ret ; Make sure this is three bytes long.
nop
pop bx
mov byte ptr [bx].takfcb+32,0 ; have to clear current record in fcb
mov ah,openf
lea dx,[bx].takfcb
int dos
cmp al,0FFH ; File not found?
jne take3
mov ah,prstr
mov dx,offset erms31
int dos
take3: inc taklev
mov takadr,bx
mov ax,word ptr [bx+16].takfcb
mov [bx].takcnt,ax
mov ax,word ptr [bx+18].takfcb
mov [bx].takcnt+2,ax ; copy size into takinfo
cmp flags.takflg,0
je take4
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
cmp byte ptr [bx].takfcb,0feh ; is it a 2.0 file handle?
jne takrd1 ; no, handle differently
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].takfcb+1 ; file handle is stored here
int dos
pop bx ; restore frame address
jmp takrd2 ; rejoin common exit
takrd1: mov ah,setdma
lea dx,[bx].takbuf
int dos
mov ah,readf
lea dx,[bx].takfcb
int dos
mov ah,setdma
lea dx,buff
int dos
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...
mov al,dosnum ; get dos version
or al,al
jne rdini4 ; post 2.0, use file handle instead...
mov bx,takadr
add bx,size takinfo ; bump take ptr, point to current take frame
lea di,[bx].takfcb ; destination is fcb
mov ax,ds
mov es,ax ; destination segment = source segment
mov si,offset ininam ; name of init file
mov cx,12 ; 8 char name + 3 char ext + 1 char drive...
rep movsb ; copy it in
mov byte ptr [bx].takfcb+32,0 ; have to clear current record in fcb
mov ah,openf
lea dx,[bx].takfcb
int dos
cmp al,0FFH ; File not found?
jne rdini1 ; no, keep going
ret ; else just return, no init file
rdini1: inc taklev ; bump take level
mov takadr,bx ; save current take frame ptr
mov ax,word ptr [bx+16].takfcb
mov [bx].takcnt,ax
mov ax,word ptr [bx+18].takfcb
mov [bx].takcnt+2,ax ; copy size into takinfo
rdini2: cmp flags.takflg,0
je rdini3
mov ah,prstr
mov dx,offset crlf
int dos
rdini3: call takrd ; Get a buffer full of data.
ret
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].takfcb+1,ax ; save file handle
mov byte ptr [di].takfcb,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
jmp rdini2 ; go rejoin common exit
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].takfcb,0ffh ; mark as a macro
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).
DELETE PROC NEAR
mov comand.cmcr,0 ; Filename must be specified.
mov ah,cmifi ; Parse an input filespec.
mov dx,offset fcb
mov bx,offset filhlp2 ; Text of help message.
call comnd
jmp r ; Bad filename.
mov ah,cmcfm ; Parse a confirm.
call comnd
jmp r
cld
mov di,offset fcb+1
mov al,'?'
mov cx,11 ; # of chars in a name
repe scasb ; are they all ?'s?
jne del1 ; no, skip message
mov dx,offset infms1
call prompt
mov ah,cmkey
mov dx,offset yestab
mov bx,0
call comnd
jmp r
push bx
mov ah,cmcfm
call comnd
pop bx
ret
nop
pop bx
cmp bx,0
jne del1
jmp rskp
del1: mov dx,offset fcb
mov ah,sfirst ; See if any files match this specification.
int dos
cmp al,0FFH ; No matches?
jne del2
mov ah,prstr
mov dx,offset erms32
int dos
jmp rskp
del2: mov dx,offset fcb
mov ah,delf ; Erase the file(s).
int dos
mov dx,offset infms8
mov ah,prstr ; Say we did so.
int dos
jmp rskp
DELETE ENDP
CHKDSK PROC NEAR
mov ah,cmcfm
call comnd
jmp r
cmp dosnum,0
je chkds1 ; yes, have to do it the hard way
mov si,offset chkdcmd ; point to cmd
mov cx,chkdlen ; and length
jmp crun ; and go execute it nicely
chkds1: push es
mov ax,ds
mov es,ax
mov di,offset fcb
mov si,offset chkfil
mov cx,chkflen
rep movsb
mov dx,offset stk + 15 ; End of stack plus roundoff.
mov cl,4
shr dx,cl ; Divide to get segment.
add dx,seg stack ; Get past the stack.
mov es,dx ; remember where segment is.
mov ah,makseg ; Create new PSP.
int dos
mov ax,siz ; Update machine size.
mov es:2,ax
mov es: byte ptr [deffcb],0 ; Blank default fcb.
mov di,deffcb+1
mov al,' ' ; Blank out fcb.
mov cx,fcbsiz
rep stosb
mov word ptr es:[terma],offset term ; Termination address.
mov es:[terma+2],cs
mov ah,openf
mov dx,offset fcb
int dos
inc al
jnz chkok
mov dx,offset erms33
mov ah,prstr
int dos
jmp chkend
chkok: mov byte ptr fcb+32,0 ; set current record field
mov di,100h ; offset to copy into
lp: mov dx,offset fcb
mov ah,readf
int dos
push ax ; save status
mov si,offset buff
mov cx,dmasiz/2 ; Word size of DMA
rep movsw ; copy into new segment...
pop ax
cmp al,1 ; End of file
je dun
cmp al,3 ; Done here too
jne lp
dun: mov ssave,sp ; Save stack pointer.
mov ax,es
mov word ptr cs:[doit+2],ax ; Set segment for CHKDSK.
mov ds,ax
mov ss,ax
mov ax,0
jmp cs: dword ptr [doit] ; Call CHKDSK.
term: mov ax,seg datas ; Reset data area.
mov ds,ax
mov sp,ssave
mov ax,seg stack
mov ss,ax
mov ah,setdma
mov dx,offset buff
int dos ; restore dma address!!
chkend: pop es
jmp rskp
doit dd 100h
CHKDSK ENDP
; Get directory listing.
DIRECT PROC NEAR
mov ah,dosver ; See what level of DOS we're at.
int dos
cmp al,0 ; Level 2.0 or above?
jne dir4 ; Yes - get directory the easy way.
mov comand.cmcr,1 ; Allow plain CR (so DIR == DIR *.*).
mov ah,cmifi ; Get input file spec.
mov dx,offset fcb ; Give the address for the FCB.
mov bx,offset filhlp3
call comnd
jmp r
mov ah,cmcfm ; Parse a confirm.
call comnd
jmp r
mov comand.cmcr,0 ; Reset this.
push es
mov ax,ds
mov es,ax
mov temp1,0FFH
mov di,offset nambuf
dir0: call getfn ; Get a matching file name.
cmp al,0FFH ; Retcode -- are we done?
je dir1 ; Yes, just leave.
call dumpit ; Print it or dump to buffer.
jmp dir0
dir1: pop es
jmp rskp
dir4: mov si,offset cmspbuf ; command processor
mov di,offset dirnam
dir5: lodsb ; get a byte
or al,al
jz dir6 ; stop on the null
stosb ; otherwise copy it in
jmp dir5 ; and keep going
dir6: mov si,offset dircmd ; add directory command to it
mov cx,dirclen
rep movsb
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 cl,ah
mov ch,0 ; length of name
sub di,offset dirnam ; compute # of bytes used
add cx,di
mov si,offset dirnam ; dir cmd
jmp crun ; join run cmd from there.
DIRECT ENDP
getfn: cmp temp1,0FFH
jne gtfn1
mov ah,sfirst ; Any matches?
mov dx,offset fcb
int dos
cmp al,0FFH ; Means no matches.
je gtfn5
call savfcb
mov temp1,0
jmp gtfn4
gtfn1: cmp flags.wldflg,0FFH ; Wilcard seen?
je gtfn2 ; Yes, get next file.
mov al,0FFH ; No, set retcode.
ret
gtfn2: call rstfcb
mov ah,snext
mov dx,offset fcb
int dos
cmp al,0 ; Any more matches?
je gtfn3 ; Yes keep going.
mov al,0FFH ; OK return code.
ret
gtfn3: call savfcb
gtfn4: push di
mov si,offset buff ; Data is here.
mov di,offset fcb ; Copy to here.
mov cx,37
repne movsb
pop di
call nicnam ; Make name nice for printing.
mov al,0
ret
gtfn5: mov ah,prstr ; Don't print if a server.
mov dx,offset erms32 ; Say no matches.
int dos
mov al,0FFH ; Failure return code.
ret
savfcb: push di
mov si,offset fcb ; Data is here.
mov di,offset cpfcb ; Copy to here.
mov cx,37
repne movsb
pop di
ret
rstfcb: push di
mov si,offset cpfcb ; Data is here.
mov di,offset fcb ; Copy to here.
mov cx,37
repne movsb
pop di
ret
nicnam: mov al,CR ; Add CRLF before print names
stosb
mov al,LF
stosb
mov cx,8
mov si,offset fcb+1
repne movsb ; Get the file name.
mov al,' '
stosb
mov cx,3
repne movsb
mov al,tab
stosb
mov al,' '
stosb
mov ah,openf
mov dx,offset fcb
int dos
mov bx,offset fcb+18 ; Get hi order word of file size.
mov ax,[bx]
mov dx,ax
mov bx,offset fcb+16 ; Get lo order word.
mov ax,[bx]
call nout2x ; Get it in decimal.
mov al,tab
stosb
mov al,' '
stosb
mov ah,0
mov si,offset fcb+20
lodsb
mov fildat+1,al
lodsb
mov fildat,al ; Date field of fcb.
mov cl,5
shr fildat+1,cl
and fildat,1
mov cl,3
shl fildat,cl
mov al,fildat
or al,fildat+1 ; Get the month field.
cmp al,9
jg nic0
push ax
mov al,' '
stosb
pop ax
nic0: call nout2 ; Make it decimal.
mov al,'-'
stosb
mov si,offset fcb+20 ; Get date field.
lodsb
and al,1FH
cmp al,10 ; Only one digit?
jge nic0x
push ax
mov al,'0' ; Make it two digits.
stosb
pop ax
nic0x: call nout2 ; Make it decimal.
mov al,'-'
stosb
mov si,offset fcb+21 ; Get the year field.
lodsb
shr al,1
add al,80
cmp al,100 ; At the year 2000 or above?
js nic0y ; No, just go on.
sub al,100 ; Go back to two digits.
nic0y: cmp al,10 ; Only one digit?
jge nic0z
push ax
mov al,'0' ; Make it two digits.
stosb
pop ax
nic0z: call nout2 ; Make it decimal.
mov al,tab
stosb
mov si,offset fcb+23 ; Get time field of fcb.
lodsb
mov cl,3 ; Get the hour field.
shr al,cl
mov tmp,'a' ; For AM.
cmp al,12 ; Before noon?
jl nic1
mov tmp,'p' ; It's PM.
je nic1 ; Don't change "12" to "0".
sub al,12 ; Use a 12 hr. clock.
nic1: cmp al,0 ; Just after midnight?
jne nic1x
add al,12 ; Make it "12" instead of "0".
nic1x: cmp al,10 ; Pad with a space?
jge nic2
push ax
mov al,' '
stosb
pop ax
nic2: call nout2 ; Make it decimal.
mov al,':' ; Separate hours and minutes.
stosb
mov si,offset fcb+23 ; Get the minutes field.
lodsb
and al,07
mov cl,3
shl al,cl
mov ah,al
mov si,offset fcb+22
lodsb
mov cl,5
shr al,cl
or al,ah
mov ah,0
cmp al,10 ; Would there be a leading zero.
jge nic3
push ax
mov al,'0'
stosb
pop ax
nic3: call nout2 ; Make it decimal.
mov al,tmp ; Add 'a' (AM) or 'p' (PM).
stosb
mov ah,closf
mov dx,offset fcb
int dos
ret
; For now, just print it.
dumpit: mov al,'$'
stosb
mov ah,prstr
mov dx,offset nambuf
int dos
mov di,offset nambuf
ret
; push to an inferior command parser
dopush proc near
cmp dosnum,0 ; < 2.0 ?
jne dopus1 ; no, go on
mov dx,offset erms34
mov ah,prstr
int dos
jmp rskp
dopus1: mov ah,cmcfm
call comnd
jmp r
mov si,offset cmspbuf ; name of parser
push si ; save beginning
sub cx,cx ; initial length
dopus2: lodsb
inc cx ; count this
or al,al ; at end?
jnz dopus2 ; no, keep going
pop si ; restore cmd
dec cx ; this is incremented one over
jmp short crun ; go run it
dopush endp
; crun - run an arbitrary program. Enter with si/address of whole
; cmd, cx/length of cmd.
CRUN proc near
push cx ; save length of cmd
mov ax,ds
mov es,ax ; address dest segment
mov di,offset nambuf
rep movsb ; copy command so we can mess with it
pop cx
mov si,offset nambuf ; point to command
jmp short run3 ; and join run code
CRUN ENDP
RUN PROC NEAR
cmp dosnum,0
jne run1
mov ah,prstr
mov dx,offset erms34 ; Complain.
int dos
jmp rskp
run1: mov ah,cmtxt ; Get program name.
mov bx,offset nambuf ; Convenient buffer.
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 cl,ah
mov ch,0
mov si,offset nambuf
; alternate entry if cmd is already known. Source cmd ptr in si
; is trashed.
run3: mov bx,cx
mov byte ptr [si+bx],cr ; end string with a cr for dos.
mov di,offset cmdnam
mov ax,ds
mov es,ax
run4: lodsb
cmp al,' '
jne run5
dec si ; back up over space
jmp short run6 ; and exit loop
run5: stosb
loop run4
run6: mov byte ptr [di],0 ; terminate string
dec si ; point back a byte into argument
mov [si],cl ; put length of argument here
mov exearg+2,si ; pointer to argument string
mov exearg+4,ds ; segment of same
inc si ; pass length over
mov al,1 ; scan leading separators
mov di,offset exefcb ; parse into this fcb
mov ah,prsfcb
int dos ; go parse the fcb
mov al,1 ; scan leading separators
mov di,offset exefcb2 ; second fcb to fill
mov ah,prsfcb
int dos ; parse the fcb
mov es,psp ; point to psp again
mov ax,es:[env] ; get environment ptr
mov exearg,ax ; put into argument block
mov bx,offset stk + 15 ; end of pgm
mov cl,4
shr bx,cl ; compute # of paragraphs in last segment
mov ax,seg stack ; end of kermit
sub ax,psp ; minus beginning...
add bx,ax ; # of paragraphs occupied
mov ah,setblk
int dos
jc run7 ; nope...
mov ax,ds
mov es,ax ; put es segment back
mov ax,offset cmdnam ; point to cmd name again
call spath ; look for it
jc run8 ; not found, go complain
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 ax,seg stack
mov ss,ax ; and stack segment
mov sp,ssave ; restore stack ptr
mov ah,setdma
mov dx,offset buff
pushf ; save flags
int dos ; restore dma address!!
popf ; recover flags
jc run8 ; error, handle.
jmp rskp ; ok, return
run7: mov ah,prstr
mov dx,offset erms36
int dos
jmp rskp
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.
; Set the maximum data packet size. [21b]
PACKLEN PROC NEAR
mov ah,trans.spsiz ; Maximum send packet size.
sub ah,4 ; Size minus control info.
sub ah,trans.chklen ; And minus checksum chars.
sub ah,2 ; Leave room at end: 2 for possible #X.
cmp trans.ebquot,'N' ; Doing 8-bit quoting?
je pack0 ; Nope so we've got our size.
cmp trans.ebquot,'Y'
je pack0 ; Not doing it in this case either.
sub ah,1 ; Another 1 for 8th-bit quoting.
pack0: mov trans.maxdat,ah ; Save max length for data field.
ret
PACKLEN ENDP
NOUT2 PROC NEAR
push ax
push dx
mov temp,10 ; Divide quotient by 10.
cwd ; Convert word to doubleword.
div temp ; AX <-- Quo, DX <-- Rem.
cmp ax,0 ; Are we done?
jz nout0 ; Yes.
call nout2 ; If not, then recurse.
nout0: add dl,'0' ; Make it printable.
mov temp,ax
mov al,dl
stosb
mov ax,temp
pop dx
pop ax
ret ; We're done. [21c]
NOUT2 ENDP
NOUT2X PROC NEAR
push ax
push dx
push cx
mov temp,10 ; Divide quotient by 10.
div temp ; AX <-- Quo, DX <-- Rem.
mov cx,dx ; Remember the remainder.
cmp ax,0 ; Are we done?
jz nout0x ; Yes.
mov dx,0
call nout2 ; If not, then recurse.
nout0x: add cl,'0' ; Make it printable.
mov temp,ax
mov al,cl
stosb
mov ax,temp
pop cx
pop dx
pop ax
ret ; We're done. [21c]
NOUT2X ENDP
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
mov si,ax
mov di,offset tfile ; place to copy to
mov dl,0 ; no '\' seen yet
mov ah,swchar ; get switch character
spath1: lodsb
stosb
cmp al,ah ; contain path characters?
jne spath2 ; no, keep going
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 embedded, keep going
pop es
mov ax,offset tfile ; else...
call isfile
mov ax,offset tfile ; point to right thing...
ret ; let isfile decide and return
; no path, keep going
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
mov al,swchar ; get switch character.
cmp byte ptr [di-1],al ; does it end with switch char?
je spath8 ; yes, don't put one 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
mov ax,bx ; not found yet, get original path
call isfile ; does it exist?
ret ; return whatever isfile says.
spath endp
isfile proc near
; returns carry off if the file pointed to by ax exists
mov dx,ax ; copy ptr
mov al,0 ; don't change anything
mov ah,chmod
int dos
ret ; dos sets carry
isfile 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
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
/bin/chmod 640 mskerm.asm
/bin/echo -n ' '; /bin/ls -ld mskerm.asm
fi
/bin/echo 'Extracting mskermit.ini'
sed 's/^X//' <<'//go.sysin dd *' >mskermit.ini
; Make shift-comma send a left angle bracket
set key scan 556
<
; Shift-period sends a right angle bracket
set key scan 558
>
; Accent grave is where ESC is supposed to be
set key scan 96
\33
; Put accent grave on the ESC function key
set key scan 27
`
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
/bin/chmod 640 mskermit.ini
/bin/echo -n ' '; /bin/ls -ld mskermit.ini
fi
/bin/echo 'Extracting mskermit.msg'
sed 's/^X//' <<'//go.sysin dd *' >mskermit.msg
This issue of the Info-Kermit Digest is devoted to the long-heralded (and
overdue) announcement of version 2 of Kermit for MS-DOS systems (Kermit
is Columbia University's file transfer protocol for use over
telecommunication lines, and it runs on a wide variety of systems). We
announced our intention to provide this new release back in January, and
have been working on it ever since. The previous release was 1.20, 28
November 1983.
The new version is called "Kermit-MS" rather than "Kermit-86" and the
version number is 2.26. It is available for several systems:
System DOS Versions
------ ------------
IBM PC, PPC, and XT 1.1, 2.0 & above
DEC Rainbow 100 and 100+ 2.05 & above
HP-150 2.0
Wang PC 2.01
Others (Generic DOS) 1.1, 2.0 & above
Versions for the IBM PCjr and Heath/Zenith 100 are soon to be added
(version 1.20 already run on these machines). If your MS-DOS system is
not on this list, you are invited to add support for it by supplying the
appropriate system- and device-dependent modules (described below).
The IBM version has been tested on IBM PCs with the old and new
motherboards and ROMs, as well as on the XT and Portable PC, on hard
disks, floppy disks, and RAM disks, and on color and monochrome monitors.
It has NOT been tested on the Compaq, Columbia, or other "PC compatible"
product; there is some chance that it might not work on the compatibles
even when the previous release (1.20) did, because of greater dependence
on the display hardware.
Version 2 of MS-DOS Kermit has been tested successfully up to 9600 baud on
the IBM, DEC, HP, and Wang micros, in communication with full duplex
systems like the DEC-20 and VAX, and half duplex systems like IBM
mainframes. Kermit-MS requires about 80K RAM, and certain functions like
PUSH and RUN will need additional memory. Thus, for DOS plus Kermit, you
will need a machine with at least 128K. Version 1.20 can run on a 64K
machine.
Version 1.20 will remain available indefinitely because it has proven
quite stable, runs on a variety of PC-compatible systems, and is
considerably smaller than version 2.
Here is a summary of the changes:
* Program organization:
The program has been broken up into separate source files, assembled
separately, and linked together. The modules are:
System/Device Independent:
MSKERM.ASM - Main program
MSSEND.ASM - File sender
MSRECV.ASM - File receiver
MSSERV.ASM - Server operation
MSFILE.ASM - File i/o
MSCMD.ASM - Command parser
MSTERM.ASM - CONNECT command
MSCOMM.ASM - Communications port buffering & flow control
MSSET.ASM - SET, SHOW, and STATUS commands
MSDEFS.H - Data structure definitions and equates
System/Device Dependent:
MSXxxx.ASM - System-dependent code for system xxx
MSYxxx.ASM - System-dependent screen and keyboard code
MSZxxx.ASM - Modem control (modem-dependent)
MSXSYS.DOC - Description of system-dependent modules
The modular organization allows easier modification of the program,
quicker transfer of modified portions from system to system. The modules
are designed to be well-defined and self-contained, such that they can
be easily replaced. For instance, someone who prefers windows and mice
to typing commands could replace the command parsing module without having
to worry about the effect on the other modules.
* Kermit Protocol Improvements:
Kermit-MS now supports:
X. 8th-bit prefixing for passing binary data through 7-bit communication links
X. 12-bit checksums and 16-bit CRCs as alternate block check types
X. Compression of repeated bytes
X. Server operation
X. Advanced commands for servers, including:
REMOTE DELETE
REMOTE DIRECTORY
REMOTE HELP
REMOTE HOST
REMOTE SPACE
REMOTE TYPE
These advanced protocol features can be used in conjunction with other
advanced Kermit implementations, including itself, as well as the current
Kermits for the DECsystem-10, DECSYSTEM-20, VAX/VMS, PDP-11 (RSX, RSTS, RT),
DEC Pro-350, and others, and soon to include IBM VM/CMS and UNIX.
* Local command execution:
The following new commands provide access to DOS functions from within the
Kermit-MS program:
DELETE
DIRECTORY
SET DEFAULT DISK
PUSH (to DOS)
SET DESTINATION (device - disk or printer)
SPACE (runs CHKDSK)
RUN (a program)
* Command parsing:
The command parser has been improved in many areas. For instance, "?" now
works much better than before (though still not perfectly). ESC now
provides completion not only in keywords, but also in filenames. CTRL-W
deletes the previous "word" on the command line. CTRL-C always returns to
the Kermit-MS> prompt.
There is a command macro facility; DEFINE lets you build macros by
combining Kermit-MS commands, DO executes them, SHOW displays them.
DOS command line arguments are accepted, and may be strung together
separated by commas, e.g. "kermit set baud 9600, set timer on, connect"
Kermit-MS now reads an initialization file, MSKERMIT.INI, and can process
(nested) command files with a TAKE command.
* Terminal emulation:
On IBM micros, the speed of Heath-19 terminal emulation has been improved
by using direct screen memory access. Functions like insert and delete
character now execute very rapidly. Heath-19 emulation functions, such as
reverse index, missing from earlier releases are now supplied. H19
emulation may be disabled to allow the use of other console drivers, like
ANSI.SYS, in conjunction with Kermit-MS.
On systems with 25 lines, the 25th line is an inverse video mode line,
displaying current settings, which may be kept or turned off. On the IBM
and DEC systems, there are pop-up help and status screens, and the screen
is saved and restored between remote/local context switches.
The terminal session can be logged to disk to provide unguarded capture of
remote files or session typescripts.
On the IBM, DEC, and HP systems, the screen can be rolled back several
pages, on a per-line or per-screen basis.
On most of the systems, print-screen (screen dump) and CTRL-print-screen
(toggle printing on/off) work as they do in DOS.
On the IBM and DEC systems, a key redefinition facility is available to
allow the layout of the keyboard to be altered to suit individual tastes,
to set up keypads or function keys for specific applications, or to
construct "keystroke macros". On IBM micros, the ALT key can be set up
for use as a META key for use with EMACS-like editors.
All versions of Kermit-MS except the generic DOS version are capable of
transmitting the BREAK signal.
The functions that are missing from the Wang and/or HP micros -- key
redefinition, pop-up menus, screen rollback, screen print -- were omitted
due to lack of information about how to get at the scan codes, screen
memory, printer interrupts, etc, and may be added at a later time.
Meanwhile, anyone out there who has the information and feels inclined to
add missing features is invited to do so.
* Communication options:
The port characteristics are left alone when Kermit-MS starts (in the
previous release, Kermit-MS always set the baud rate). The program allows
settings for speed, duplex, flow control, handshake, and parity on a
per-port basis, to allow convenient switching between ports.
* File Transfer:
You can now supply new names for files in SEND and GET commands.
A timeout facility has been added to allow automatic recovery from
deadlocks when communicating with systems (like IBM mainframes) that can't
time out.
The file transfer display has been reformatted, and includes more useful
information, including a percentage for outbound files. The various
counts are updated more reliably.
Several options are available for interrupting file transfer, including
^X (cancel current file), ^Z (cancel entire batch), ^E (user-generated
"error"), ^C (return immediately to command level), CR (simulate a timeout).
The options are displayed during file transfer.
There is a new end-of-file option to allow selection of DOS-style (believe
the DOS byte count) or CP/M-style (file ends at first CTRL-Z) EOF detection.
* Remote operation:
Kermit-MS may be run from the back port in either interactive or server mode.
This allows micro-to-micro file transfer without requiring an operator on
both ends.
* New Bootstrapping Procedure:
The Kermit .EXE files for the various systems are now encoded using a
printable 4-for-3 encoding, with compression of repeated 0 bytes. The
result tends to be smaller than the original .EXE file. A new set of
bootstrapping programs has been provided:
MSMKBOO.C Encode. Can be used on any binary file. Written in C.
MSBOOT.FOR Send the encoded file from the mainframe. Fortran.
MSPCTRAN.BAS Decode the encoded file on the micro. MS Basic.
MSPCBOOT.BAS Receive on the micro, decode on the fly. MS Basic.
* Documentation:
There's an entirely new manual, available now as a separate document,
soon to be incorporated into the Kermit User Guide. It describes
operation of the program in detail, along with the new bootstrapping
procedure.
* How To Get It:
Kermit is available for a wide variety of systems -- micros, minis, and
mainframes. It is distributed by Columbia University via network or on
magnetic tape. For further information about Kermit, send network mail to
INFO-KERMIT-REQUEST@COLUMBIA-20, or write to the Kermit Distribution
address below. To be added to the Info-Kermit network mailing list, mail
to INFO-KERMIT-REQUEST@COLUMBIA-20.
The new MS-DOS Kermit files are available from COLUMBIA-20 via anonymous
FTP after 6pm daily (ARPANET), though KERMSRV at CUVMA on BITNET (BITNET
users should type "SMSG RSCS MSG CUVMA KERMSRV HELP" for information about
the Columbia Kermit file server), and on all the Columbia DEC-20 systems
in the KERMIT area. The file names all begin with "MS" (on BITNET, omit
the "KER:" prefix).
The executable programs have the suffix .EXE and are in 8-bit binary
format. The corresponding 7-bit ASCII encoded files have the suffix .BOO.
The system-specific programs are available in both .EXE and .BOO formats.
KER:MSIBMPC -- IBM PC, XT
KER:MSIBMJR -- IBM PCjr (not yet availble)
KER:MSRB100 -- DEC Rainbow 100, 100+
KER:MSHP150 -- Hewlett-Packard 150
KER:MSHZ100 -- Heath/Zenith 100 (not yet available)
KER:MSWANG -- Wang PC
KER:MSGENER -- Generic DOS
KER:MS*.ASM, KER:MS*.H are the assembler source files.
KER:MSBUILD.HLP tells how to build the program.
KER:MSKERMIT.DOC is the new MS-DOS section for the Kermit User Guide.
KER:MSKERMIT.MSS is the Scribe source for the .DOC file.
Those without network access may write to the following address for
details of how to order a complete Kermit distribution on 9-track
magnetic tape:
KERMIT Distribution
Columbia University Center for Computing Activities
612 West 115th Street
New York, NY 10025
Version 2 of MS-DOS Kermit will be submitted to PC-SIG so that it can be
ordered on IBM PC floppy disks. Inquiries should be directed to
PC Software Interest Group
1556 Halford Avenue, Suite #130
Santa Clara, CA 95051
Phone 408-730-9291
Be sure to wait until they have version 2, because they are presently
distributing version 1 on their disks numbers 41 and 42. It may take
some time for them to update their distribution.
* Credit:
The bulk of the work was done by Daphne Tzoar and Jeff Damens of the
Columbia University Center for Computing Activities. Many ideas (and
"existence proofs") were contributed by Herm Fischer of Litton Data
Systems -- key redefinitions, remote and server operation, etc, but those
who have been using Herm's modified 1.20 will find that some of the
features he added have been done differently in this release. 8th-bit
quoting was originally added by Leslie Spira and her staff at The Source
Telecomputing to allow Kermit to transfer binary files over Telenet. The
new bootstrapping procedure and the new file transfer display were done by
Bill Catchings of Columbia. Filename completion came from Kimmo Laaksonen
at the Helsinki University of Technology. Some corporate support and
encouragement was provided by Digital Equipment Corporation, Wang
Laboratories, and IBM.
* Disclaimer:
Although we have been using the new version on several different kinds
of systems for a good while and have done extensive testing, some bugs
may have slipped through. Please hang on to your old release (1.20),
and don't hesitate to report any problems to Info-Kermit@COLUMBIA-20.
Suggestions and contributions are also welcome.
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
/bin/chmod 640 mskermit.msg
/bin/echo -n ' '; /bin/ls -ld mskermit.msg
fi
/bin/echo 'Extracting msmeta.ini'
sed 's/^X//' <<'//go.sysin dd *' >msmeta.ini
; TAKE'ing this file will make the ALT key function like a META key
; on the IBM PC.
set key scan 2064
\321
set key scan 2065
\327
set key scan 2066
\305
set key scan 2067
\322
set key scan 2068
\324
set key scan 2069
\331
set key scan 2070
\325
set key scan 2071
\311
set key scan 2072
\317
set key scan 2073
\320
set key scan 2078
\301
set key scan 2079
\323
set key scan 2080
\304
set key scan 2081
\306
set key scan 2082
\307
set key scan 2083
\310
set key scan 2084
\312
set key scan 2086
\313
set key scan 2087
\314
set key scan 2168
\261
set key scan 2169
\262
set key scan 2170
\263
set key scan 2171
\264
set key scan 2172
\265
set key scan 2173
\266
set key scan 2174
\267
set key scan 2175
\270
set key scan 2176
\271
set key scan 2177
\272
set key scan 2092
\332
set key scan 2093
\330
set key scan 2094
\303
set key scan 2095
\326
set key scan 2096
\302
set key scan 2097
\316
set key scan 2098
\317//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
/bin/chmod 640 msmeta.ini
/bin/echo -n ' '; /bin/ls -ld msmeta.ini
fi
X/bin/echo 'Extracting msrbemacs.ini'
sed 's/^X//' <<'//go.sysin dd *' >msrbemacs.ini
; EMACS function key setup for Kermit-MS/Rainbow
;
; C-@ (set mark) on SELECT
set key select
\00
;
; M-h (select region) on CTRL-SELECT
set key scan 1313
\33h
;
; C-U 12 C-X C-I (rigidly indent region 12 spaces) on TAB
set key scan 9
\25\61\62\30\11
; C-X C-I (rigidly indent region) on SHIFT-TAB
set key scan 521
\30\11
;
; C-S (forward search) on FIND
set key find
\23
;
; C-R (reverse search) on CTRL-FIND
set key scan 1307
\22
;
; M-D (delete word) on REMOVE
set key remove
\33d
;
; M-K (delete sentence) on CTRL-REMOVE
set key scan 1311
\33k
;
; C-P (up line) on uparrow
set key scan 295
\20
;
; M-[ (up paragraph) on CTRL-uparrow
set key scan 1319
\33[
;
; C-X [ (up page) on SHIFT-uparrow
set key scan 807
\30[
;
; M-< (top of file) on CTRL-SHIFT-uparrow
set key scan 1831
\33<
;
; C-B (back character) on leftarrow
set key scan 301
\02
;
; C-A (beginning of line) on CTRL-leftarrow
set key scan 1325
\01
;
; M-A (back sentence) on SHIFT-leftarrow
set key scan 813
\33a
;
; C-N (next line) on downarrow
set key scan 297
\16
; M-] (down paragraph) on CTRL-downarrow
set key scan 1321
\33]
;
; C-X ] (down page) on SHIFT-downarrow
set key scan 809
\30]
;
; M-> (end of file) on CTRL-SHIFT-downarrow
set key scan 1833
\30>
;
; C-F (forward character) on rightarrow
set key scan 299
\06
;
; C-E (end of line) on CTRL-rightarrow
set key scan 1323
\05
;
; M-E (end of sentence) on SHIFT-rightarrow
set key scan 811
\33e
;
; C-X E (do keyboard macro) on DO
set key scan 257
\30e
;
; C-U C-X E (do keyboard macro 4x) on CTRL-DO
set key scan 1281
\25\30e
;
; C-U 8 C-X E (do keyboard macro 8x) on SHIFT-DO
set key scan 769
\25\70\30e
;
; C-U C-U C-X E (do keyboard macro 16x) on CTRL-SHIFT-DO
set key scan 1793
\25\25\30e
;
; ^_ on HELP
set key scan 256
\37
;
; C-X C-Z on EXIT
set key scan 271
\30\32
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
/bin/chmod 640 msrbemacs.ini
/bin/echo -n ' '; /bin/ls -ld msrbemacs.ini
fi
X/bin/echo 'Extracting msrecv.asm'
sed 's/^X//' <<'//go.sysin dd *' >msrecv.asm
public read12, read2, rin21, rfile3, read, updrtr, nak, rrinit
include msdefs.h
datas segment public 'datas'
extrn fcb:byte, data:byte, bufpnt:word, chrcnt:word, curchk:byte
extrn comand:byte, flags:byte, pack:byte, trans:byte
ermes7 db '?Unable to receive initiate$'
ermes8 db '?Unable to receive file name$'
ermes9 db '?Unable to receive end of file$'
erms10 db '?Unable to receive data$'
infms1 db cr,' Receiving: In progress$'
infms3 db 'Completed$'
infms4 db 'Failed$'
infms6 db 'Interrupted$'
remmsg1 db 'Kermit-MS: Invalid filename'
filhlp2 db ' Confirm with carriage return or specify name '
db ' to use for incoming file $'
ender db bell,bell,'$'
crlf db cr,lf,'$'
temp dw 0
datas ends
code segment public
extrn gofil:near, outbuf:near, fixfcb:near, comnd:near
extrn spack:near, rpack:near, serini:near, serrst:near
extrn spar:near, rpar:near, init:near, init1:near, cxmsg:near
extrn error:near, ptchr:near, erpos:near, rtpos:near
extrn stpos:near, rprpos:near, nppos:near, nout:near
extrn dodec:near, doenc:near, errpack:near
extrn send11:near, clrmod:near
assume cs:code, ds:datas
; Update retry count and fall through to send a NAK.
nak0: call updrtr ; Update retry count.
nak: mov ax,pack.pktnum ; Get the packet number we're waiting for.
mov pack.argblk,ax
mov pack.argbk1,0
mov cx,0 ; No data, but this may change.
call doenc ; So call encode.
mov ah,'N' ; NAK that packet.
call spack
jmp abort
nop ; So 'jmp rskp' in SPACK comes here. [19a]
ret ; Go around again.
updrtr: cmp pack.state,'A' ; Supposed to abort?
je upd0 ; Yes, don't bother with retry count.
inc pack.numrtr ; Increment the number of retries.
cmp flags.xflg,1 ; Writing to screen?
je upd0
call rtpos ; Position cursor.
mov ax,pack.numrtr
call nout ; Write the number of retries.
upd0: ret
; Abort
ABORT PROC NEAR
mov pack.state,'A' ; Otherwise abort.
ret
ABORT ENDP
; init variables for read...
rrinit proc near
mov pack.numpkt,0 ; Set the number of packets to zero.
mov pack.numrtr,0 ; Set the number of retries to zero.
mov pack.pktnum,0 ; Set the packet number to zero.
mov pack.numtry,0 ; Set the number of tries to zero.
ret
rrinit endp
; RECEIVE command -- Some code moved to the GET routine. [21a]
READ PROC NEAR
mov comand.cmrflg,1 ; Say we're receiving a file. [21a start]
mov comand.cmcr,1 ; Allow bare CR after RECEIVE.
mov flags.droflg,0 ; Override default drive flag.
mov flags.nmoflg,0 ; Override file name from other host?
mov dx,offset fcb ; Put filename here.
mov bx,offset filhlp2 ; Text of help message.
mov ah,cmifi ; Read in the filename.
call comnd
jmp r
mov comand.cmrflg,0 ; Reset flag.
mov comand.cmcr,0
mov flags.wldflg,0 ; Just in case
mov ah,cmcfm ; Get a confirm.
call comnd
jmp r
read1: cmp flags.remflg,0 ; remote mode?
jne read12 ; yes, no printing
call init
read12: mov flags.cxzflg,0 ; Reset ^X/^Z flag. [20c]
call rrinit ; init variables for read
call serini ; Initialize serial port. [14]
cmp flags.remflg,0 ; in remote mode?
jne read12a ; yes, no printing
call init1 ; Clear the line and initialize the buffers.
call stpos
mov ah,prstr ; Be informative.
mov dx,offset infms1
int dos
call rtpos ; Position cursor.
mov ax,pack.numrtr
call nout ; Write the number of retries.
read12a:mov pack.state,'R' ; Set the state to receive initiate.
read2: cmp flags.xflg,1 ; Are we receiving to the screen. [21c]
je read21 ; Skip the screen stuff. [21c]
cmp flags.remflg,0 ; maybe remote mode?
jne read21 ; yup, skip the screen stuff
call nppos ; Position cursor for number of packets msg.
mov ax,pack.numpkt
call nout ; Write the number of packets.
read21: mov ah,pack.state ; Get the state. [21c]
cmp ah,'D' ; Are we in the data send state?
jne read3
call rdata
jmp read2
read3: cmp ah,'F' ; Are we in the file receive state?
jne read4
call rfile ; Call receive file.
jmp read2
read4: cmp ah,'R' ; Are we in the receive initiate state?
jne read5
call rinit
jmp read2
read5: cmp ah,'C' ; Are we in the receive complete state?
jne read6
call serrst ; Reset serial port. [14]
cmp flags.xflg,0 ; Did we write to the screen? [21c]
je read51 ; No so print status. [21c]
mov flags.xflg,0 ; Reset it. [21c]
jmp rskp ; Yes, so just return. [21c]
read51: cmp flags.remflg,0 ; remote mode?
jne read51a ; yes, keep going
call stpos ; Position cursor. [21c]
mov ah,prstr
mov dx,offset infms3 ; Plus a little cuteness.
cmp flags.cxzflg,0 ; Completed or interrupted? [20c]
je read13 ; Ended normally. [20c]
mov dx,offset infms6 ; Say was interrupted. [20c]
read13: int dos
cmp flags.belflg,0 ; Bell desired? [17a]
je readnb ; No. [17a]
mov dx,offset ender ; Ring them bells. [4]
int dos ; [4]
readnb: call clrmod ; clear 25th line
call rprpos ; Put prompt here.
read51a:jmp rskp
read6: call serrst ; Reset serial port. [14]
cmp flags.xflg,0 ; Did we write out to screen? [21c]
je read61 ; No so print status. [21c]
mov flags.xflg,0 ; Reset it. [21c]
jmp rskp ; Print onto screen. [21c]
read61: cmp flags.remflg,0 ; remote mode?
jne read7a ; yes, no printing.
call stpos ; Position cursor. [21c]
mov ah,prstr
mov dx,offset infms4 ; Plus a little cuteness.
int dos
cmp flags.belflg,0 ; Bell desired? [17a]
je read7 ; No. [17a]
mov dx,offset ender ; Ring them bells. [4]
int dos ; [4]
read7: call clrmod ; clear mode line
call rprpos ; Put prompt here.
read7a: jmp rskp
READ ENDP
; Receive routines
; Receive init
RINIT PROC NEAR
mov ah,pack.numtry ; Get the number of tries.
cmp ah,imxtry ; Have we reached the maximum number of tries?
jl rinit2
call erpos ; Position cursor.
mov dx,offset ermes7
mov ah,prstr
int dos ; Print an error message.
mov bx,dx
call errpack ; Send error packet just in case.
jmp abort ; Change the state to abort.
rinit2: inc ah ; Increment it.
mov pack.numtry,ah ; Save the updated number of tries.
mov ax,pack.argbk2 ; get packet type if here from get
cmp flags.getflg,1 ; Have we already read in the packet? [21a]
je rin21a ; Yes, so don't call RPACK. [21a]
mov ah,trans.chklen
mov curchk,ah ; Save checksum length we want to use.
mov trans.chklen,1 ; Use 1 char for init packet.
call rpack ; Get a packet.
jmp rin22 ; Trashed packet: nak, retry.
push ax
mov ah,curchk
mov trans.chklen,ah ; Reset to desired value.
pop ax
rin21a: cmp ah,'S' ; Is it a send initiate packet?
jne rinit3 ; If not see if its an error.
rin21: mov flags.getflg,0 ; Reset flag. [21a]
mov ah,pack.numtry ; Get the number of tries.
mov pack.oldtry,ah ; Save it.
mov pack.numtry,0 ; Reset the number of tries.
mov ax,pack.argblk ; Returned packet number. (Synchronize them.)
inc ax ; Increment it.
and ax,3FH ; Turn off the two high order bits.
mov pack.pktnum,ax ; Save modulo 64 of the number.
mov bx,pack.numpkt
inc bx ; Increment the number of packets.
mov pack.numpkt,bx
mov ax,pack.argbk1 ; Get the number of arguments received.
mov bx,offset data ; Get a pointer to the data.
call spar ; Get the data into the proper variables.
mov bx,offset data ; Get a pointer to our data block.
call rpar ; Set up the receive parameters.
xchg ah,al
mov ah,0
mov pack.argbk1,ax ; Store the returned number of arguments.
mov ah,trans.chklen ; Checksum length we'll use.
mov curchk,ah ; Save it.
mov trans.chklen,1 ; Use 1 char for init packet.
mov ah,'Y' ; Acknowledge packet.
call spack ; Send the packet.
jmp abort
mov ah,curchk ; Checksum length we'll use.
mov trans.chklen,ah ; Reset to desired value.
mov ah,'F' ; Set the state to file send.
mov pack.state,ah
ret
rin22: mov ah,curchk
mov trans.chklen,ah ; Reset to desired value.
jmp nak0 ; Try again.
rinit3: cmp ah,'E' ; Is it an error packet?
jne rinit4
call error
rinit4: jmp abort
RINIT ENDP
; Receive file
RFILE PROC NEAR
cmp pack.numtry,maxtry ; Have we reached the maximum number of tries?
jl rfile1
call erpos ; Position cursor.
mov dx,offset ermes8
mov ah,prstr
int dos ; Print an error message.
mov bx,dx
call errpack ; Send error packet just in case.
jmp abort ; Change the state to abort.
rfile1: inc pack.numtry ; Save the updated number of tries.
call rpack ; Get a packet.
jmp nak0 ; Trashed packet: nak, retry.
cmp ah,'S' ; Is it a send initiate packet?
je rfil10
call dodec ; Decode all incoming packets.
jmp rfile2 ; No, try next type.
rfil10: cmp pack.oldtry,imxtry ; Have we reached the maximum number of tries?
jl rfil12 ; If not proceed.
call erpos ; Position cursor.
mov dx,offset ermes7
mov ah,prstr
int dos ; Print an error message.
mov bx,dx
call errpack ; Send error packet just in case.
jmp abort ; Change the state to abort.
rfil12: inc pack.oldtry ; Save the updated number of tries.
mov ax,pack.pktnum ; Get the present packet number.
cmp ax,0 ; Had we wrapped around? [18 start]
jne rfilx
mov ax,64
rfilx: dec ax ; Decrement. [18 end -- new label]
cmp ax,pack.argblk ; Is the packet's number one less than now?
je rfil13
jmp nak0 ; No, NAK and try again.
rfil13: call updrtr ; Update retry count.
mov pack.numtry,0 ; Reset the number of tries.
mov bx,offset data ; Get a pointer to our data block.
call rpar ; Set up the parameter information.
xchg ah,al
mov ah,0
mov pack.argbk1,ax ; Save the number of arguments.
mov ah,'Y' ; Acknowledge packet.
call spack ; Send the packet.
jmp abort
ret
rfile2: cmp ah,'Z' ; Is it an EOF packet?
jne rfile3 ; No, try next type.
cmp pack.oldtry,maxtry ; Have we reached the maximum number of tries?
jl rfil21 ; If not proceed.
call erpos ; Position cursor.
mov dx,offset ermes9
mov ah,prstr
int dos ; Print an error message.
mov bx,dx
call errpack ; Send error packet just in case.
jmp abort ; Change the state to abort.
rfil21: inc pack.oldtry ; Increment it.
mov ax,pack.pktnum ; Get the present packet number.
cmp ax,0 ; Had we wrapped around? [18 start]
jne rfily
mov ax,64
rfily: dec ax ; Decrement. [18 end -- new label]
cmp ax,pack.argblk ; Is the packet's number one less than now?
je rfil24
jmp nak0 ; No, NAK and try again.
rfil24: call updrtr ; Update retry count.
mov pack.numtry,0
mov pack.argbk1,0 ; No data. (The packet number is in argblk.)
mov cx,0
call doenc
mov ah,'Y' ; Acknowledge packet.
call spack ; Send the packet.
jmp abort
ret
rfile3: cmp ah,'F' ; Start of file?
je rfil31 ; Yes. [21c]
cmp ah,'X' ; Text header packet? [21c]
jne rfile4 ; Neither one.
rfil31: mov ax,pack.argblk ; Get the packet number. [21c]
cmp ax,pack.pktnum ; Is it the right packet number?
je rfil32
jmp nak ; No, NAK it and try again.
rfil32: inc ax ; Increment the packet number.
and ax,3FH ; Turn off the two high order bits.
mov pack.pktnum,ax ; Save modulo 64 of the number.
inc pack.numpkt ; Increment the number of packets.
call gofil ; Get a file to write to.
jmp abort
call init1 ; Initialize all the buffers.
mov ah,pack.numtry ; Get the number of tries.
mov pack.oldtry,ah ; Save it.
mov pack.numtry,0 ; Reset the number of tries.
mov pack.argbk1,0 ; No data. (The packet number is in argblk.)
mov cx,0
call doenc
mov ah,'Y' ; Acknowledge packet.
call spack ; Send the packet.
jmp abort
mov pack.state,'D' ; Set the state to data receive.
ret
rfile4: cmp ah,'B' ; End of transmission.
jne rfile5
mov ax,pack.pktnum
cmp ax,pack.argblk ; Do we match?
je rfil41
jmp nak ; No, NAK it and try again.
rfil41: mov pack.argbk1,0 ; No data. (Packet number already in argblk).
mov cx,0
call doenc
mov ah,'Y' ; Acknowledge packet.
call spack ; Send the packet.
jmp abort
mov pack.state,'C' ; Set the state to complete.
ret
rfile5: cmp ah,'E' ; Is it an error packet.
jne rfile6
call error
rfile6: jmp abort
RFILE ENDP
; Receive data
RDATA PROC NEAR
cmp pack.numtry,maxtry ; Get the number of tries.
jl rdata1
call erpos ; Position cursor.
mov dx,offset erms10
mov ah,prstr
int dos ; Print an error message.
mov bx,dx
call errpack ; Send error packet just in case.
jmp abort ; Change the state to abort.
rdata1: inc pack.numtry ; Save the updated number of tries.
call rpack ; Get a packet.
jmp nak0 ; Trashed packet: nak, retry.
cmp ah,'D' ; Is it a data packet?
je rdat11
call dodec ; Decode data.
jmp rdata2 ; No, try next type.
rdat11: mov ax,pack.pktnum ; Get the present packet number.
cmp ax,pack.argblk ; Is the packet's number correct?
jz rdat14
cmp pack.oldtry,maxtry ; Have we reached the maximum number of tries?
jl rdat12 ; If not proceed.
call erpos ; Position cursor.
mov dx,offset erms10
mov ah,prstr
int dos ; Print an error message.
mov bx,dx
call errpack ; Send error packet just in case.
jmp abort ; Change the state to abort.
rdat12: inc pack.oldtry ; Save the updated number of tries.
mov ax,pack.pktnum
cmp ax,0 ; Had we wrapped around? [18 start]
jne rdatx
mov ax,64
rdatx: dec ax ; [14] [18 end -- new label]
cmp ax,pack.argblk ; Is the packet's number one less than now?
je rdat13
jmp nak0 ; No, NAK it and try again.
rdat13: call updrtr ; Update retry count.
mov pack.numtry,0 ; Reset number of tries.
mov pack.argbk1,0 ; No data. (The packet number is in argblk.)
mov cx,0
call doenc
mov ah,'Y' ; Acknowledge packet.
call spack ; Send the packet.
jmp abort
ret
rdat14: inc ax ; Increment the packet number.
and ax,3FH ; Turn off the two high order bits.
mov pack.pktnum,ax ; Save modulo 64 of the number.
inc pack.numpkt ; Increment the number of packets.
mov ah,pack.numtry ; Get the number of tries.
mov pack.oldtry,ah ; Save it.
mov ax,pack.argbk1 ; Get the length of the data.
cmp flags.cxzflg,0 ; Has the user typed a ^X or ^Z? [20c]
je rdt14x ; No, write out the data.
cmp flags.abfflg,1 ; Discard incomplete files?
je rdat15 ; If yes don't write data out to file. [20c]
rdt14x: mov bx,offset data ; Where the data is. [25]
call ptchr
jmp abort ; Unable to write out chars; abort.
rdat15: mov pack.numtry,0 ; Reset the number of tries.
mov pack.argbk1,0 ; No data. (Packet number still in argblk.)
mov cx,0
cmp flags.cxzflg,0 ; Interrupt file transfer? [20c]
je rdat16 ; Nope. [20c]
mov bx,offset data ; Send data in ACK in case remote... [20c]
mov ah,flags.cxzflg ; ... knows about ^X/^Z. [20c]
mov [bx],ah ; Put data into the packet. [20c]
mov pack.argbk1,1 ; Set data size to 1. [20c]
mov cx,1
rdat16: call doenc
mov ah,'Y' ; Acknowledge packet.
call spack ; Send the packet.
jmp abort
ret
rdata2: cmp ah,'F' ; Start of file?
je rdat20 ; Yup. [21c]
cmp ah,'X' ; Text header packet? [21c]
jne rdata3 ; No, try next type.
rdat20: cmp pack.oldtry,maxtry ; Reached the max number of tries? [21c]
jl rdat21 ; If not proceed.
call erpos ; Position cursor.
mov dx,offset ermes8
mov ah,prstr
int dos ; Print an error message.
mov bx,dx
call errpack ; Send error packet just in case.
jmp abort ; Change the state to abort.
rdat21: inc pack.oldtry ; Save the updated number of tries.
mov ax,pack.pktnum
cmp ax,0 ; Had we wrapped around? [18 start]
jne rdaty
mov ax,64
rdaty: dec ax ; [14 Omitted accidentally - D.T.] [18 end]
cmp ax,pack.argblk ; Is the packet's number one less than now?
je rdat22
jmp nak0 ; No, NAK it and try again.
rdat22: call updrtr ; Update retry count.
mov pack.numtry,0 ; Reset number of tries.
mov pack.argbk1,0 ; No data. (The packet number is in argblk.)
mov cx,0
call doenc
mov ah,'Y' ; Acknowledge packet.
call spack ; Send the packet.
jmp abort
ret
rdata3: cmp ah,'Z' ; Is it a EOF packet?
je rdat3x ; [13]
jmp rdata4 ; Try and see if its an error. [13]
rdat3x: mov ax,pack.pktnum ; Get the present packet number. [13]
cmp ax,pack.argblk ; Is the packet's number correct?
je rdat32
jmp nak0 ; No, NAK it and try again.
rdat32: inc ax ; Increment the packet number.
and ax,3FH ; Turn off the two high order bits.
mov pack.pktnum,ax ; Save modulo 64 of the number.
inc pack.numpkt
cmp flags.cxzflg,0 ; Do we want to discard the file? [20c]
jne rdt32x ; Yes. [20c]
cmp pack.argbk1,1 ; One piece of data? [20c]
jne rdat33 ; Nope - finish writing out file? [20c]
mov bx,offset data ; Get data area. [20c]
mov ah,[bx] ; Get the data. [20c]
cmp ah,'D' ; "D" for discard? [20c]
jne rdat33 ; Nope - write out file. [20c]
rdt32x: cmp flags.abfflg,0 ; Keep incomplete files?
je rdat33 ; Yes, go write it out.
mov ah,closf ; First, close the file.
mov dx,offset fcb ; Give the file parameters. [20c]
int dos ; Kill it, ignore errors. [20c]
mov ah,delf ; Delete the file if opened. [20c]
int dos
cmp flags.cxzflg,'X' ; Kill one file or all? [20c]
jne rdat36 ; No so leave flag alone. [20c]
call cxmsg ; Clear msg about interrupt. [20c]
mov flags.cxzflg,0 ; Reset - ^X only kills one file. [20c]
jmp rdat36
rdat33: mov bx,bufpnt ; Get the dma pointer.
mov ax,80H
sub ax,chrcnt ; Get the number of chars left in the DMA.
cmp flags.eofcz,0 ; should we write a ^Z?
jz rdat35 ; no, keep going
cmp flags.xflg,0 ; writing to a file?
jne rdat35 ; no, skip ^Z
cmp ax,80H ; [13 start]
jne rdat34
call outbuf ; Write out buffer if no room for ^Z.
jmp abort
mov ax,0 ; [13 end]
inc chrcnt ; Increment size by one (not two). [21b]
rdat34: mov cl,'Z'-100O ; Put in a ^Z for EOF.
mov [bx],cl ; Add it. [21c]
inc ax
dec chrcnt
rdat35: mov cx,chrcnt
mov temp,cx
call outbuf ; Output the last buffer.
jmp abort ; Give up if the disk is full.
mov ax,temp ; Prepare for the function call.
call fixfcb
mov ah,closf ; Close up the file.
mov dx,offset fcb
int dos
rdat36: cmp flags.destflg,1 ; Writing to disk?
je rdat37 ; Yes, skip next part.
cmp flags.xflg,1 ; Writing to screen?
je rdat37 ; Yes, skip this part.
mov dl,ff ; Send a form feed.
mov ah,lstout ; Write out to first printer.
int dos
rdat37: mov ah,pack.numtry ; Get the number of tries.
mov pack.oldtry,ah ; Save it.
mov pack.numtry,0 ; Reset the number of tries.
mov pack.argbk1,0 ; No data. (The packet number is in argblk.)
mov cx,0
call doenc
mov ah,'Y' ; Acknowledge packet.
call spack ; Send the packet.
jmp abort
mov pack.state,'F'
ret
rdata4: cmp ah,'E' ; Is it an error packet.
jne rdata5
call error
rdata5: jmp abort
RDATA 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
R PROC NEAR
ret
R ENDP
code ends
end
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
/bin/chmod 640 msmeta.ini
/bin/echo -n ' '; /bin/ls -ld msmeta.ini
fi
/bin/echo 'Extracting msrecv.asm'
sed 's/^X//' <<'//go.sysin dd *' >msrecv.asm
public read12, read2, rin21, rfile3, read, updrtr, nak, rrinit
include msdefs.h
datas segment public 'datas'
extrn fcb:byte, data:byte, bufpnt:word, chrcnt:word, curchk:byte
extrn comand:byte, flags:byte, pack:byte, trans:byte
ermes7 db '?Unable to receive initiate$'
ermes8 db '?Unable to receive file name$'
ermes9 db '?Unable to receive end of file$'
erms10 db '?Unable to receive data$'
infms1 db cr,' Receiving: In progress$'
infms3 db 'Completed$'
infms4 db 'Failed$'
infms6 db 'Interrupted$'
remmsg1 db 'Kermit-MS: Invalid filename'
filhlp2 db ' Confirm with carriage return or specify name '
db ' to use for incoming file $'
ender db bell,bell,'$'
crlf db cr,lf,'$'
temp dw 0
datas ends
code segment public
extrn gofil:near, outbuf:near, fixfcb:near, comnd:near
extrn spack:near, rpack:near, serini:near, serrst:near
extrn spar:near, rpar:near, init:near, init1:near, cxmsg:near
extrn error:near, ptchr:near, erpos:near, rtpos:near
extrn stpos:near, rprpos:near, nppos:near, nout:near
extrn dodec:near, doenc:near, errpack:near
extrn send11:near, clrmod:near
assume cs:code, ds:datas
; Update retry count and fall through to send a NAK.
nak0: call updrtr ; Update retry count.
nak: mov ax,pack.pktnum ; Get the packet number we're waiting for.
mov pack.argblk,ax
mov pack.argbk1,0
mov cx,0 ; No data, but this may change.
call doenc ; So call encode.
mov ah,'N' ; NAK that packet.
call spack
jmp abort
nop ; So 'jmp rskp' in SPACK comes here. [19a]
ret ; Go around again.
updrtr: cmp pack.state,'A' ; Supposed to abort?
je upd0 ; Yes, don't bother with retry count.
inc pack.numrtr ; Increment the number of retries.
cmp flags.xflg,1 ; Writing to screen?
je upd0
call rtpos ; Position cursor.
mov ax,pack.numrtr
call nout ; Write the number of retries.
upd0: ret
; Abort
ABORT PROC NEAR
mov pack.state,'A' ; Otherwise abort.
ret
ABORT ENDP
; init variables for read...
rrinit proc near
mov pack.numpkt,0 ; Set the number of packets to zero.
mov pack.numrtr,0 ; Set the number of retries to zero.
mov pack.pktnum,0 ; Set the packet number to zero.
mov pack.numtry,0 ; Set the number of tries to zero.
ret
rrinit endp
; RECEIVE command -- Some code moved to the GET routine. [21a]
READ PROC NEAR
mov comand.cmrflg,1 ; Say we're receiving a file. [21a start]
mov comand.cmcr,1 ; Allow bare CR after RECEIVE.
mov flags.droflg,0 ; Override default drive flag.
mov flags.nmoflg,0 ; Override file name from other host?
mov dx,offset fcb ; Put filename here.
mov bx,offset filhlp2 ; Text of help message.
mov ah,cmifi ; Read in the filename.
call comnd
jmp r
mov comand.cmrflg,0 ; Reset flag.
mov comand.cmcr,0
mov flags.wldflg,0 ; Just in case
mov ah,cmcfm ; Get a confirm.
call comnd
jmp r
read1: cmp flags.remflg,0 ; remote mode?
jne read12 ; yes, no printing
call init
read12: mov flags.cxzflg,0 ; Reset ^X/^Z flag. [20c]
call rrinit ; init variables for read
call serini ; Initialize serial port. [14]
cmp flags.remflg,0 ; in remote mode?
jne read12a ; yes, no printing
call init1 ; Clear the line and initialize the buffers.
call stpos
mov ah,prstr ; Be informative.
mov dx,offset infms1
int dos
call rtpos ; Position cursor.
mov ax,pack.numrtr
call nout ; Write the number of retries.
read12a:mov pack.state,'R' ; Set the state to receive initiate.
read2: cmp flags.xflg,1 ; Are we receiving to the screen. [21c]
je read21 ; Skip the screen stuff. [21c]
cmp flags.remflg,0 ; maybe remote mode?
jne read21 ; yup, skip the screen stuff
call nppos ; Position cursor for number of packets msg.
mov ax,pack.numpkt
call nout ; Write the number of packets.
read21: mov ah,pack.state ; Get the state. [21c]
cmp ah,'D' ; Are we in the data send state?
jne read3
call rdata
jmp read2
read3: cmp ah,'F' ; Are we in the file receive state?
jne read4
call rfile ; Call receive file.
jmp read2
read4: cmp ah,'R' ; Are we in the receive initiate state?
jne read5
call rinit
jmp read2
read5: cmp ah,'C' ; Are we in the receive complete state?
jne read6
call serrst ; Reset serial port. [14]
cmp flags.xflg,0 ; Did we write to the screen? [21c]
je read51 ; No so print status. [21c]
mov flags.xflg,0 ; Reset it. [21c]
jmp rskp ; Yes, so just return. [21c]
read51: cmp flags.remflg,0 ; remote mode?
jne read51a ; yes, keep going
call stpos ; Position cursor. [21c]
mov ah,prstr
mov dx,offset infms3 ; Plus a little cuteness.
cmp flags.cxzflg,0 ; Completed or interrupted? [20c]
je read13 ; Ended normally. [20c]
mov dx,offset infms6 ; Say was interrupted. [20c]
read13: int dos
cmp flags.belflg,0 ; Bell desired? [17a]
je readnb ; No. [17a]
mov dx,offset ender ; Ring them bells. [4]
int dos ; [4]
readnb: call clrmod ; clear 25th line
call rprpos ; Put prompt here.
read51a:jmp rskp
read6: call serrst ; Reset serial port. [14]
cmp flags.xflg,0 ; Did we write out to screen? [21c]
je read61 ; No so print status. [21c]
mov flags.xflg,0 ; Reset it. [21c]
jmp rskp ; Print onto screen. [21c]
read61: cmp flags.remflg,0 ; remote mode?
jne read7a ; yes, no printing.
call stpos ; Position cursor. [21c]
mov ah,prstr
mov dx,offset infms4 ; Plus a little cuteness.
int dos
cmp flags.belflg,0 ; Bell desired? [17a]
je read7 ; No. [17a]
mov dx,offset ender ; Ring them bells. [4]
int dos ; [4]
read7: call clrmod ; clear mode line
call rprpos ; Put prompt here.
read7a: jmp rskp
READ ENDP
; Receive routines
; Receive init
RINIT PROC NEAR
mov ah,pack.numtry ; Get the number of tries.
cmp ah,imxtry ; Have we reached the maximum number of tries?
jl rinit2
call erpos ; Position cursor.
mov dx,offset ermes7
mov ah,prstr
int dos ; Print an error message.
mov bx,dx
call errpack ; Send error packet just in case.
jmp abort ; Change the state to abort.
rinit2: inc ah ; Increment it.
mov pack.numtry,ah ; Save the updated number of tries.
mov ax,pack.argbk2 ; get packet type if here from get
cmp flags.getflg,1 ; Have we already read in the packet? [21a]
je rin21a ; Yes, so don't call RPACK. [21a]
mov ah,trans.chklen
mov curchk,ah ; Save checksum length we want to use.
mov trans.chklen,1 ; Use 1 char for init packet.
call rpack ; Get a packet.
jmp rin22 ; Trashed packet: nak, retry.
push ax
mov ah,curchk
mov trans.chklen,ah ; Reset to desired value.
pop ax
rin21a: cmp ah,'S' ; Is it a send initiate packet?
jne rinit3 ; If not see if its an error.
rin21: mov flags.getflg,0 ; Reset flag. [21a]
mov ah,pack.numtry ; Get the number of tries.
mov pack.oldtry,ah ; Save it.
mov pack.numtry,0 ; Reset the number of tries.
mov ax,pack.argblk ; Returned packet number. (Synchronize them.)
inc ax ; Increment it.
and ax,3FH ; Turn off the two high order bits.
mov pack.pktnum,ax ; Save modulo 64 of the number.
mov bx,pack.numpkt
inc bx ; Increment the number of packets.
mov pack.numpkt,bx
mov ax,pack.argbk1 ; Get the number of arguments received.
mov bx,offset data ; Get a pointer to the data.
call spar ; Get the data into the proper variables.
mov bx,offset data ; Get a pointer to our data block.
call rpar ; Set up the receive parameters.
xchg ah,al
mov ah,0
mov pack.argbk1,ax ; Store the returned number of arguments.
mov ah,trans.chklen ; Checksum length we'll use.
mov curchk,ah ; Save it.
mov trans.chklen,1 ; Use 1 char for init packet.
mov ah,'Y' ; Acknowledge packet.
call spack ; Send the packet.
jmp abort
mov ah,curchk ; Checksum length we'll use.
mov trans.chklen,ah ; Reset to desired value.
mov ah,'F' ; Set the state to file send.
mov pack.state,ah
ret
rin22: mov ah,curchk
mov trans.chklen,ah ; Reset to desired value.
jmp nak0 ; Try again.
rinit3: cmp ah,'E' ; Is it an error packet?
jne rinit4
call error
rinit4: jmp abort
RINIT ENDP
; Receive file
RFILE PROC NEAR
cmp pack.numtry,maxtry ; Have we reached the maximum number of tries?
jl rfile1
call erpos ; Position cursor.
mov dx,offset ermes8
mov ah,prstr
int dos ; Print an error message.
mov bx,dx
call errpack ; Send error packet just in case.
jmp abort ; Change the state to abort.
rfile1: inc pack.numtry ; Save the updated number of tries.
call rpack ; Get a packet.
jmp nak0 ; Trashed packet: nak, retry.
cmp ah,'S' ; Is it a send initiate packet?
je rfil10
call dodec ; Decode all incoming packets.
jmp rfile2 ; No, try next type.
rfil10: cmp pack.oldtry,imxtry ; Have we reached the maximum number of tries?
jl rfil12 ; If not proceed.
call erpos ; Position cursor.
mov dx,offset ermes7
mov ah,prstr
int dos ; Print an error message.
mov bx,dx
call errpack ; Send error packet just in case.
jmp abort ; Change the state to abort.
rfil12: inc pack.oldtry ; Save the updated number of tries.
mov ax,pack.pktnum ; Get the present packet number.
cmp ax,0 ; Had we wrapped around? [18 start]
jne rfilx
mov ax,64
rfilx: dec ax ; Decrement. [18 end -- new label]
cmp ax,pack.argblk ; Is the packet's number one less than now?
je rfil13
jmp nak0 ; No, NAK and try again.
rfil13: call updrtr ; Update retry count.
mov pack.numtry,0 ; Reset the number of tries.
mov bx,offset data ; Get a pointer to our data block.
call rpar ; Set up the parameter information.
xchg ah,al
mov ah,0
mov pack.argbk1,ax ; Save the number of arguments.
mov ah,'Y' ; Acknowledge packet.
call spack ; Send the packet.
jmp abort
ret
rfile2: cmp ah,'Z' ; Is it an EOF packet?
jne rfile3 ; No, try next type.
cmp pack.oldtry,maxtry ; Have we reached the maximum number of tries?
jl rfil21 ; If not proceed.
call erpos ; Position cursor.
mov dx,offset ermes9
mov ah,prstr
int dos ; Print an error message.
mov bx,dx
call errpack ; Send error packet just in case.
jmp abort ; Change the state to abort.
rfil21: inc pack.oldtry ; Increment it.
mov ax,pack.pktnum ; Get the present packet number.
cmp ax,0 ; Had we wrapped around? [18 start]
jne rfily
mov ax,64
rfily: dec ax ; Decrement. [18 end -- new label]
cmp ax,pack.argblk ; Is the packet's number one less than now?
je rfil24
jmp nak0 ; No, NAK and try again.
rfil24: call updrtr ; Update retry count.
mov pack.numtry,0
mov pack.argbk1,0 ; No data. (The packet number is in argblk.)
mov cx,0
call doenc
mov ah,'Y' ; Acknowledge packet.
call spack ; Send the packet.
jmp abort
ret
rfile3: cmp ah,'F' ; Start of file?
je rfil31 ; Yes. [21c]
cmp ah,'X' ; Text header packet? [21c]
jne rfile4 ; Neither one.
rfil31: mov ax,pack.argblk ; Get the packet number. [21c]
cmp ax,pack.pktnum ; Is it the right packet number?
je rfil32
jmp nak ; No, NAK it and try again.
rfil32: inc ax ; Increment the packet number.
and ax,3FH ; Turn off the two high order bits.
mov pack.pktnum,ax ; Save modulo 64 of the number.
inc pack.numpkt ; Increment the number of packets.
call gofil ; Get a file to write to.
jmp abort
call init1 ; Initialize all the buffers.
mov ah,pack.numtry ; Get the number of tries.
mov pack.oldtry,ah ; Save it.
mov pack.numtry,0 ; Reset the number of tries.
mov pack.argbk1,0 ; No data. (The packet number is in argblk.)
mov cx,0
call doenc
mov ah,'Y' ; Acknowledge packet.
call spack ; Send the packet.
jmp abort
mov pack.state,'D' ; Set the state to data receive.
ret
rfile4: cmp ah,'B' ; End of transmission.
jne rfile5
mov ax,pack.pktnum
cmp ax,pack.argblk ; Do we match?
je rfil41
jmp nak ; No, NAK it and try again.
rfil41: mov pack.argbk1,0 ; No data. (Packet number already in argblk).
mov cx,0
call doenc
mov ah,'Y' ; Acknowledge packet.
call spack ; Send the packet.
jmp abort
mov pack.state,'C' ; Set the state to complete.
ret
rfile5: cmp ah,'E' ; Is it an error packet.
jne rfile6
call error
rfile6: jmp abort
RFILE ENDP
; Receive data
RDATA PROC NEAR
cmp pack.numtry,maxtry ; Get the number of tries.
jl rdata1
call erpos ; Position cursor.
mov dx,offset erms10
mov ah,prstr
int dos ; Print an error message.
mov bx,dx
call errpack ; Send error packet just in case.
jmp abort ; Change the state to abort.
rdata1: inc pack.numtry ; Save the updated number of tries.
call rpack ; Get a packet.
jmp nak0 ; Trashed packet: nak, retry.
cmp ah,'D' ; Is it a data packet?
je rdat11
call dodec ; Decode data.
jmp rdata2 ; No, try next type.
rdat11: mov ax,pack.pktnum ; Get the present packet number.
cmp ax,pack.argblk ; Is the packet's number correct?
jz rdat14
cmp pack.oldtry,maxtry ; Have we reached the maximum number of tries?
jl rdat12 ; If not proceed.
call erpos ; Position cursor.
mov dx,offset erms10
mov ah,prstr
int dos ; Print an error message.
mov bx,dx
call errpack ; Send error packet just in case.
jmp abort ; Change the state to abort.
rdat12: inc pack.oldtry ; Save the updated number of tries.
mov ax,pack.pktnum
cmp ax,0 ; Had we wrapped around? [18 start]
jne rdatx
mov ax,64
rdatx: dec ax ; [14] [18 end -- new label]
cmp ax,pack.argblk ; Is the packet's number one less than now?
je rdat13
jmp nak0 ; No, NAK it and try again.
rdat13: call updrtr ; Update retry count.
mov pack.numtry,0 ; Reset number of tries.
mov pack.argbk1,0 ; No data. (The packet number is in argblk.)
mov cx,0
call doenc
mov ah,'Y' ; Acknowledge packet.
call spack ; Send the packet.
jmp abort
ret
rdat14: inc ax ; Increment the packet number.
and ax,3FH ; Turn off the two high order bits.
mov pack.pktnum,ax ; Save modulo 64 of the number.
inc pack.numpkt ; Increment the number of packets.
mov ah,pack.numtry ; Get the number of tries.
mov pack.oldtry,ah ; Save it.
mov ax,pack.argbk1 ; Get the length of the data.
cmp flags.cxzflg,0 ; Has the user typed a ^X or ^Z? [20c]
je rdt14x ; No, write out the data.
cmp flags.abfflg,1 ; Discard incomplete files?
je rdat15 ; If yes don't write data out to file. [20c]
rdt14x: mov bx,offset data ; Where the data is. [25]
call ptchr
jmp abort ; Unable to write out chars; abort.
rdat15: mov pack.numtry,0 ; Reset the number of tries.
mov pack.argbk1,0 ; No data. (Packet number still in argblk.)
mov cx,0
cmp flags.cxzflg,0 ; Interrupt file transfer? [20c]
je rdat16 ; Nope. [20c]
mov bx,offset data ; Send data in ACK in case remote... [20c]
mov ah,flags.cxzflg ; ... knows about ^X/^Z. [20c]
mov [bx],ah ; Put data into the packet. [20c]
mov pack.argbk1,1 ; Set data size to 1. [20c]
mov cx,1
rdat16: call doenc
mov ah,'Y' ; Acknowledge packet.
call spack ; Send the packet.
jmp abort
ret
rdata2: cmp ah,'F' ; Start of file?
je rdat20 ; Yup. [21c]
cmp ah,'X' ; Text header packet? [21c]
jne rdata3 ; No, try next type.
rdat20: cmp pack.oldtry,maxtry ; Reached the max number of tries? [21c]
jl rdat21 ; If not proceed.
call erpos ; Position cursor.
mov dx,offset ermes8
mov ah,prstr
int dos ; Print an error message.
mov bx,dx
call errpack ; Send error packet just in case.
jmp abort ; Change the state to abort.
rdat21: inc pack.oldtry ; Save the updated number of tries.
mov ax,pack.pktnum
cmp ax,0 ; Had we wrapped around? [18 start]
jne rdaty
mov ax,64
rdaty: dec ax ; [14 Omitted accidentally - D.T.] [18 end]
cmp ax,pack.argblk ; Is the packet's number one less than now?
je rdat22
jmp nak0 ; No, NAK it and try again.
rdat22: call updrtr ; Update retry count.
mov pack.numtry,0 ; Reset number of tries.
mov pack.argbk1,0 ; No data. (The packet number is in argblk.)
mov cx,0
call doenc
mov ah,'Y' ; Acknowledge packet.
call spack ; Send the packet.
jmp abort
ret
rdata3: cmp ah,'Z' ; Is it a EOF packet?
je rdat3x ; [13]
jmp rdata4 ; Try and see if its an error. [13]
rdat3x: mov ax,pack.pktnum ; Get the present packet number. [13]
cmp ax,pack.argblk ; Is the packet's number correct?
je rdat32
jmp nak0 ; No, NAK it and try again.
rdat32: inc ax ; Increment the packet number.
and ax,3FH ; Turn off the two high order bits.
mov pack.pktnum,ax ; Save modulo 64 of the number.
inc pack.numpkt
cmp flags.cxzflg,0 ; Do we want to discard the file? [20c]
jne rdt32x ; Yes. [20c]
cmp pack.argbk1,1 ; One piece of data? [20c]
jne rdat33 ; Nope - finish writing out file? [20c]
mov bx,offset data ; Get data area. [20c]
mov ah,[bx] ; Get the data. [20c]
cmp ah,'D' ; "D" for discard? [20c]
jne rdat33 ; Nope - write out file. [20c]
rdt32x: cmp flags.abfflg,0 ; Keep incomplete files?
je rdat33 ; Yes, go write it out.
mov ah,closf ; First, close the file.
mov dx,offset fcb ; Give the file parameters. [20c]
int dos ; Kill it, ignore errors. [20c]
mov ah,delf ; Delete the file if opened. [20c]
int dos
cmp flags.cxzflg,'X' ; Kill one file or all? [20c]
jne rdat36 ; No so leave flag alone. [20c]
call cxmsg ; Clear msg about interrupt. [20c]
mov flags.cxzflg,0 ; Reset - ^X only kills one file. [20c]
jmp rdat36
rdat33: mov bx,bufpnt ; Get the dma pointer.
mov ax,80H
sub ax,chrcnt ; Get the number of chars left in the DMA.
cmp flags.eofcz,0 ; should we write a ^Z?
jz rdat35 ; no, keep going
cmp flags.xflg,0 ; writing to a file?
jne rdat35 ; no, skip ^Z
cmp ax,80H ; [13 start]
jne rdat34
call outbuf ; Write out buffer if no room for ^Z.
jmp abort
mov ax,0 ; [13 end]
inc chrcnt ; Increment size by one (not two). [21b]
rdat34: mov cl,'Z'-100O ; Put in a ^Z for EOF.
mov [bx],cl ; Add it. [21c]
inc ax
dec chrcnt
rdat35: mov cx,chrcnt
mov temp,cx
call outbuf ; Output the last buffer.
jmp abort ; Give up if the disk is full.
mov ax,temp ; Prepare for the function call.
call fixfcb
mov ah,closf ; Close up the file.
mov dx,offset fcb
int dos
rdat36: cmp flags.destflg,1 ; Writing to disk?
je rdat37 ; Yes, skip next part.
cmp flags.xflg,1 ; Writing to screen?
je rdat37 ; Yes, skip this part.
mov dl,ff ; Send a form feed.
mov ah,lstout ; Write out to first printer.
int dos
rdat37: mov ah,pack.numtry ; Get the number of tries.
mov pack.oldtry,ah ; Save it.
mov pack.numtry,0 ; Reset the number of tries.
mov pack.argbk1,0 ; No data. (The packet number is in argblk.)
mov cx,0
call doenc
mov ah,'Y' ; Acknowledge packet.
call spack ; Send the packet.
jmp abort
mov pack.state,'F'
ret
rdata4: cmp ah,'E' ; Is it an error packet.
jne rdata5
call error
rdata5: jmp abort
RDATA 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
R PROC NEAR
ret
R ENDP
code ends
end
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
/bin/chmod 640 msrecv.asm
/bin/echo -n ' '; /bin/ls -ld msrecv.asm
fi
knutson@ut-ngp.UUCP (Jim Knutson) (10/05/84)
: Run this shell script with "sh" not "csh" PATH=:/bin:/usr/bin:/usr/ucb export PATH all=FALSE if [ $1x = -ax ]; then all=TRUE fi /bin/echo 'Extracting mssend.asm' sed 's/^X//' <<'//go.sysin dd *' >mssend.asm public spar, rpar, error, error1, nout, send, flags, trans, pack public dodec, doenc, curchk, inichk, packlen, send11 include msdefs.h spmin equ 20 ; Minimum packet size. spmax equ 94 ; Maximum packet size. datas segment public 'datas' extrn buff:byte, data:byte, fcb:byte, cpfcb:byte, filbuf:byte extrn decbuf:byte, chrcnt:word, bufpnt:word, comand:byte extrn rptq:byte, origr:byte, rptct:byte, rptval:byte flags flginfo <> trans trinfo <> pack pktinfo <> crlf db cr,lf,'$' ender db bell,bell,'$' ; [4] erms14 db '?Unable to receive an acknowledgment from the host$' erms15 db '?Unable to find file$' erms20 db 'Unable to send init packet$' erms21 db 'Unable to send file header$' erms22 db 'Unable to send data$' erms23 db 'Unable to send end-of-file packet$' erms24 db 'Unable to send break packet$' infms2 db cr,' Sending: In progress$' infms3 db 'Completed$' infms4 db 'Failed$' infms6 db 'Interrupted$' infms7 db cr,' Percent transferred: 100%$' remmsg1 db 'Kermit-MS: File not found$' filhlp db ' Input file spec (possibly wild) $' filmsg db ' File name to use on target system or confirm with' db ' a carriage return $' curchk db 0 ; Use to store checksum length. inichk db 1 ; Original or set checksum length. chrptr dw ? ; Position in character buffer. fcbpt dw ? ; Position in FCB. datptr dw ? ; Position in packet data buffer. siz dw ? ; Size of data from gtchr. temp dw 0 temp4 dw 0 sendas dw 50 dup(0) ; Buffer for file name. difnam db 0 ; Send under different name? difsiz db 0 ; Size of new file name. asmsg db ' as $' datas ends code segment public extrn serini:near, serrst:near, comnd:near, init:near extrn spack:near, rpack:near, gtnfil:near, gtchr:near extrn getfil:near, clrfln:near, nppos:near, rprpos:near extrn erpos:near, rtpos:near, cxmsg:near, stpos:near extrn encode:near, nulref:near, decode:near, nulr:near extrn errpack:near, updrtr:near, clrmod:near, fcbcpy:near extrn perpos:near assume cs:code,ds:datas ; This routine sets up the data for init packet (either the ; Send_init or ACK packet). RPAR PROC NEAR mov ah,trans.rpsiz ; Get the receive packet size. add ah,' ' ; Add a space to make it printable. mov [bx],ah ; Put it in the packet. mov ah,trans.rtime ; Get the receive packet time out. add ah,' ' ; Add a space. mov 1[bx],ah ; Put it in the packet. mov ah,trans.rpad ; Get the number of padding chars. add ah,' ' mov 2[bx],ah ; Put it in the packet. mov ah,trans.rpadch ; Get the padding char. add ah,100O ; Uncontrol it. and ah,7FH mov 3[bx],ah ; Put it in the packet. mov ah,trans.reol ; Get the EOL char. add ah,' ' mov 4[bx],ah ; Put it in the packet. mov ah,trans.rquote ; Get the quote char. mov 5[bx],ah ; Put it in the packet. mov ah,trans.ebquot ; Get 8-bit quote char. [21b] mov 6[bx],ah ; Add it to the packet. [21b] mov ah,trans.chklen ; Length of checksum. add ah,48 ; Make into a real digit. mov 7[bx],ah mov ah,rptq ; Repeat quote char. cmp ah,0 ; Null means no. jne rpar0 mov ah,' ' ; Send a blank instead. rpar0: mov 8[bx],ah mov ah,09H ; Nine pieces of data. ret RPAR ENDP ; This routine reads in all the send_init packet information. SPAR PROC NEAR cmp ax,1 jge sparx mov ah,dspsiz ; Data not supplied by host, use default. jmp sparx2 sparx: mov temp4,ax ; Save the number of arguments. mov ah,trans.spsiz cmp ah,dspsiz ; Is current value the default? jne sparx2 ; No, assume changed by user. mov ah,[bx] ; Get the max packet size. sub ah,' ' ; Subtract a space. cmp ah,spmin ; Can't be below the minimum. jge sparx1 mov ah,spmin jmp sparx2 sparx1: cmp ah,spmax ; Or above the maximum. jle sparx2 mov ah,spmax sparx2: mov trans.spsiz,ah ; Save it. mov ax,temp4 cmp al,2 ; Fewer than two pieces? jge spar0 mov ah,dstime ; Data not supplied by host, use default. jmp spar02 spar0: mov ah,trans.stime cmp ah,dstime ; Is current value the default? jne spar02 ; No, assume changed by user. mov ah,1[bx] ; Get the timeout value. sub ah,' ' ; Subtract a space. cmp ah,0 ja spar01 ; Must be non-negative. mov ah,0 spar01: cmp ah,trans.rtime ; Same as other side's timeout. jne spar02 add ah,5 ; If so, make it a little different. spar02: mov trans.stime,ah ; Save it. mov ax,temp4 cmp al,3 ; Fewer than three pieces? jge spar1 mov ah,dspad ; Data not supplied by host, use default. jmp spar11 spar1: mov ah,trans.spad cmp ah,dspad ; Is current value the default? jne spar11 ; No, assume changed by user. mov ah,2[bx] ; Get the number of padding chars. sub ah,' ' cmp ah,0 ja spar11 ; Must be non-negative. mov ah,0 spar11: mov trans.spad,ah mov ax,temp4 cmp al,4 ; Fewer than four pieces? jge spar2 mov ah,dspadc ; Data not supplied by host, use default. jmp spar21 spar2: mov ah,trans.spadch cmp ah,dspadc ; Is current value the default? jne spar21 ; No, assume changed by user. mov ah,3[bx] ; Get the padding char. add ah,100O ; Re-controlify it. and ah,7FH cmp ah,del ; Delete? je spar21 ; Yes, then it's OK. cmp ah,0 jge spar20 mov ah,0 ; Below zero is no good. jmp spar21 ; Use zero (null). spar20: cmp ah,31 ; Is it a control char? jle spar21 ; Yes, then OK. mov ah,0 ; No, use null. spar21: mov trans.spadch,ah mov ax,temp4 cmp al,5 ; Fewer than five pieces? jge spar3 mov ah,dseol ; Data not supplied by host, use default. jmp spar31 spar3: mov ah,trans.seol cmp ah,dseol ; Is current value the default? jne spar31 ; No, assume changed by user. mov ah,4[bx] ; Get the EOL char. sub ah,' ' cmp ah,0 jge spar30 ; Cannot be negative. mov ah,cr ; If it is, use default of carriage return. jmp spar31 spar30: cmp ah,31 ; Is it a control char? jle spar31 ; Yes, then use it. mov ah,cr ; Else, use the default. spar31: mov trans.seol,ah mov ax,temp4 cmp al,6 ; Fewer than six pieces? jge spar4 mov ah,dsquot ; Data not supplied by host, use default. jmp spar41 spar4: mov ah,trans.squote cmp ah,dsquot ; Is current value the default? jne spar41 ; No, assume changed by user. mov ah,5[bx] ; Get the quote char. cmp ah,' ' ; Less than a space? jge spar40 mov ah,dsquot ; Yes, use default. jmp spar41 spar40: cmp ah,'~' ; Must also be less then a tilde. jle spar41 mov ah,dsquot ; Else, use default. spar41: mov trans.squote,ah cmp al,7 ; Fewer than seven pieces? [21b begin] jge spar5 mov trans.ebquot,'Y' ; Data not supplied by host, use default. jmp spar51 spar5: mov ah,6[bx] ; Get other sides 8-bit quote request. call doquo ; And set quote char. [21b end] spar51: cmp al,8 ; Fewer than eight pieces? jge spar6 mov trans.chklen,1 jmp spar61 spar6: mov ah,inichk mov trans.chklen,ah ; Checksum length we really want to use. mov ah,7[bx] ; Get other sides checksum length. call dochk ; Determine what size to use. spar61: cmp al,9 ; Fewer than nine pieces? jge spar7 mov rptq,0 ret spar7: mov ah,8[bx] ; Get other sides repeat count prefix. mov ch,drpt mov rptq,0 call dorpt ret SPAR ENDP ; Set 8-bit quote character based on my capabilities and the other ; Kermit's request. [21b] DOQUO PROC NEAR cmp trans.ebquot,'N' ; Can I do 8-bit quoting at all? je dq3 ; No - so forget it. cmp trans.ebquot,'Y' ; Can I do it if requested? jne dq0 ; No - it's a must that I do it. mov trans.ebquot,ah ; Do whatever he wants. jmp dq1 dq0: cmp ah,'Y' ; I need quoting - can he do it? je dq1 ; Yes - then all is settled. cmp ah,'N' ; No - then don't quote. je dq3 cmp ah,trans.ebquot ; Both need quoting - chars must match. jne dq3 dq1: mov ah,trans.ebquot cmp ah,'Y' ; If Y or N, don't validate prefix. je dq2 cmp ah,'N' je dq2 call prechk ; Is it in range 33-62, 96-126? mov ah,'Y' ; Failed, don't do quoting. nop cmp ah,trans.rquote ; Same prefix? je dq3 ; Not allowed, so don't do quoting. cmp ah,trans.squote ; Same prefix here? je dq3 ; This is illegal too. mov trans.ebquot,ah ; Remember what we decided on. dq2: ret dq3: mov trans.ebquot,'N' ; Quoting will not be done. ret DOQUO ENDP ; Check if prefix in AH is in the proper range: 33-62, 96-126. ; RSKP if so else RETURN. prechk: cmp ah,33 jge prec0 ; It's above 33. ret prec0: cmp ah,62 jg prec1 jmp rskp ; And below 62. OK. prec1: cmp ah,96 jge prec2 ; It's above 96. ret prec2: cmp ah,126 jg prec3 jmp rskp ; And below 126. OK. prec3: ret ; Set checksum length. dochk: cmp ah,'1' ; Must be 1, 2, or 3. jl doc1 cmp ah,'3' jle doc2 doc1: mov ah,'1' doc2: sub ah,48 ; Don't want it printable. cmp ah,trans.chklen ; Do we want the same thing? je dochk0 ; Yes, then we're done. mov trans.chklen,1 ; No, use single character checksum. dochk0: ret ; Just return for now. ; Set repeat count quote character. The one used must be different than ; the control and eight-bit quote characters. Also, both sides must ; use the same character. dorpt: call prechk ; Is it in the valid range? mov ah,0 ; No, don't use their value. nop cmp ah,trans.squote ; Same as the control quote char? je dorpt0 ; Yes, that's illegal, no repeats. cmp ah,trans.rquote ; How about this one? je dorpt0 ; No good. cmp ah,trans.ebquot ; Same as eight bit quote char? je dorpt0 ; Yes, that's illegal too, no repeats. cmp ah,ch ; Are we planning to use the same char? jne dorpt0 ; No, that's no good either. mov rptq,ch ; Use repeat quote char now. dorpt0: ret ; Send command SEND PROC NEAR mov comand.cmcr,0 ; Filename must be specified. mov difnam,0 ; Assume we'll use original filename. mov flags.wldflg,0 ; Re-initialize every time. mov ah,cmifi ; Parse an input file spec. mov dx,offset fcb ; Give the address for the FCB. mov bx,offset filhlp ; Text of help message. call comnd jmp r ; Give up on bad parse. cmp flags.wldflg,0FFH ; Any wildcards seen? je send1 ; Yes, get a confirm. mov bx,offset sendas ; See if want to send file under dif name. mov dx,offset filmsg ; In case user needs help. mov ah,cmtxt call comnd jmp r cmp ah,0 ; Different name supplied? je send11 ; No - keep as it. mov difnam,1 ; Yes - send different filename. mov difsiz,ah ; Remember length of new name. jmp send11 send1: mov ah,cmcfm call comnd ; Get a confirm. jmp r ; Didn't get a confirm. send11: mov flags.droflg,0 ; Reset flags from fn parsing. [21a] mov flags.nmoflg,0 ; Reset flags from fn parsing. [21a] mov ah,sfirst ; Get the first file. mov dx,offset fcb int dos cmp al,0FFH ; Any found? jne send12 cmp pack.state,'R' ; was this from a remote GET? jne sen11a ; no, print error and continue mov bx,offset remmsg1 ; else get error message call errpack ; go complain jmp abort ; and abort this sen11a: mov ah,prstr mov dx,offset crlf int dos mov ah,prstr mov dx,offset erms15 int dos ret send12: cmp flags.wldflg,0 ; Any wildcards. [7 start] je send16 ; Nope, so no problem. mov bx,offset fcb ; Remember what FCB looked like. mov di,offset cpfcb mov cl,37 ; Size of FCB. call fcbcpy mov di,offset fcb+1 ; Copy filename from DTA to FCB. mov bx,offset buff+1 mov cl,11 call fcbcpy ; [7 end] send16: call serini ; Initialize serial port. [14] mov pack.pktnum,0 ; Set the packet number to zero. mov pack.numtry,0 ; Set the number of tries to zero. mov pack.numpkt,0 ; Set the number of packets to zero. mov pack.numrtr,0 ; Set the number of retries to zero. mov pack.state,'S' ; Set the state to receive initiate. cmp flags.remflg,0 ; remote mode? jne send2a ; yes, continue below. call init ; Clear the line and initialize the buffers. call rtpos ; Position cursor. mov ax,0 call nout ; Write the number of retries. call stpos ; Print status of file transfer. mov ah,prstr ; Be informative. mov dx,offset infms2 int dos send2: cmp flags.remflg,0 ; remote mode? jne send2a ; yes, skip printing call nppos ; Number of packets sent. mov ax,pack.numpkt call nout ; Write the packet number. send2a: cmp pack.state,'D' ; Are we in the data send state? jne send3 call sdata jmp send2 send3: cmp pack.state,'F' ; Are we in the file send state? jne send4 call sfile ; Call send file. jmp send2 send4: cmp pack.state,'Z' ; Are we in the EOF state? jne send5 call seof jmp send2 send5: cmp pack.state,'S' ; Are we in the send initiate state? jne send6 call sinit jmp send2 send6: cmp pack.state,'B' ; Are we in the eot state? jne send7 call seot jmp send2 send7: cmp pack.state,'C' ; Are we in the send complete state? jne send8 call serrst ; Reset serial port. [14] cmp flags.remflg,0 ; remote mode? jne send7a ; yes, no printing. cmp flags.cxzflg,0 ; completed normally? jne send7b ; no, don't bother with this call perpos mov ah,prstr mov dx,offset infms7 int dos send7b: call stpos mov ah,prstr mov dx,offset infms3 ; Plus a little cuteness. cmp flags.cxzflg,0 ; Completed or interrupted? je snd71 ; Ended normally. mov dx,offset infms6 ; Say was interrupted. snd71: int dos ; New label. cmp flags.belflg,0 ; Bell desired? [17a] je sendnb ; [17a] mov dx,offset ender ; Ring them bells. [4] int dos sendnb: call clrmod call rprpos send7a: jmp rskp send8: call serrst ; Reset serial port. [14] cmp flags.remflg,0 ; remote mode? jne send9a ; no, no printing. call stpos mov ah,prstr mov dx,offset infms4 ; Plus a little cuteness. int dos cmp flags.belflg,0 ; Bell desired? [17a] je send9 ; No. [17a] mov dx,offset ender ; Ring them bells. [4] int dos ; [4] send9: call clrmod call rprpos send9a: jmp rskp SEND ENDP ; Send routines ; Send initiate SINIT PROC NEAR cmp pack.numtry,imxtry ; Have we reached the maximum number of tries? jl sinit2 call erpos mov dx,offset erms14 mov ah,prstr int dos ; Print an error message. mov bx,offset erms20 call errpack ; Send error packet just in case. jmp abort ; Change the state to abort. sinit2: inc pack.numtry ; Save the updated number of tries. mov bx,offset data ; Get a pointer to our data block. call rpar ; Set up the parameter information. xchg ah,al mov ah,0 mov pack.argbk1,ax ; Save the number of arguments. mov ax,pack.numpkt ; Get the packet number. mov pack.argblk,ax mov ah,trans.chklen mov curchk,ah ; Store checksum length we want to use. mov trans.chklen,1 ; Send init checksum is always 1 char. mov ah,'S' ; Send initiate packet. call spack ; Send the packet. jmp abort call rpack ; Get a packet. jmp sini23 ; Trashed packet don't change state, retry. push ax mov ah,curchk mov trans.chklen,ah ; Checksum length we want to use. pop ax cmp ah,'Y' ; ACK? jne sinit3 ; If not try next. mov ax,pack.pktnum ; Get the packet number. cmp ax,pack.argblk ; Is it the right packet number? je sini22 ret ; If not try again. sini22: inc ax ; Increment the packet number. and ax,3FH ; Turn off the two high order bits. mov pack.pktnum,ax ; Save modulo 64 of the number. inc pack.numpkt ; Increment the number of packets. mov ax,pack.argbk1 ; Get the number of pieces of data. mov bx,offset data ; Pointer to the data. call spar ; Read in the data. call packlen ; Get max send packet size. [21b] mov ah,pack.numtry ; Get the number of tries. mov pack.oldtry,ah ; Save it. mov pack.numtry,0 ; Reset the number of tries. mov pack.state,'F' ; Set the state to file send. call getfil ; Open the file. jmp abort ; Something is wrong, die. ret sini23: mov ah,curchk ; Restore desired checksum length. mov trans.chklen,ah call updrtr ; Update retry counter. ret ; And retry. sinit3: cmp ah,'N' ; NAK? jne sinit4 ; If not see if its an error. call rtpos ; Position cursor. inc pack.numrtr ; Increment the number of retries mov ax,pack.numrtr call nout ; Write the number of retries. ret sinit4: cmp ah,'E' ; Is it an error packet. jne sinit5 call error sinit5: jmp abort SINIT ENDP ; Send file header SFILE PROC NEAR cmp pack.numtry,maxtry ; Have we reached the maximum number of tries? jl sfile1 call erpos mov dx,offset erms14 mov ah,prstr int dos ; Print an error message. mov bx,offset erms21 call errpack ; Send error packet just in case. jmp abort ; Change the state to abort. sfile1: inc pack.numtry ; Increment it. mov flags.cxzflg,0 ; Clear ^X,^Z flag. mov datptr,offset data ; Get a pointer to our data block. mov bx,offset fcb+1 ; Pointer to file name in FCB. mov fcbpt,bx ; Save position in FCB. mov cl,0 ; Counter for chars in file name. mov ch,0 ; Counter for number of chars in FCB. sfil11: cmp ch,8H ; Ninth char? jne sfil12 mov ah,'.' mov bx,datptr mov [bx],ah ; Put dot in data packet. inc bx mov datptr,bx ; Save new position in data packet. inc cl sfil12: inc ch cmp ch,0CH ; Twelve? jns sfil13 mov bx,fcbpt mov ah,[bx] ; Get char of filename. inc bx mov fcbpt,bx ; Save position in FCB. cmp ah,'!' ; Is it a good char? jl sfil11 ; If not, get the next. mov bx,datptr mov [bx],ah ; Put char in data buffer. inc cl ; Increment counter. inc bx mov datptr,bx ; Save new position. jmp sfil11 ; Get another char. sfil13: mov ch,0 cmp flags.remflg,0 ; remote mode? jne sfil13a ; yes, no printing. push cx ; Don't forget the size. mov bx,datptr mov ah,'$' mov [bx],ah ; Put dollar sign for printing. call clrfln mov ah,prstr mov dx,offset data ; Print file name. int dos pop cx sfil13a:cmp difnam,0 ; Sending file under different name. je sfl13x ; No, so don't give new name. call newfn sfl13x: call doenc ; Do encoding. mov ax,pack.pktnum ; Get the packet number. mov pack.argblk,ax mov ah,'F' ; File header packet. call spack ; Send the packet. jmp abort call rpack ; Get a packet. jmp tryagn ; Trashed packet don't change state, retry. call dodec ; Do all decoding. cmp ah,'Y' ; ACK? jne sfile2 ; If not try next. mov ax,pack.pktnum ; Get the packet number. cmp ax,pack.argblk je sfil14 ret ; If not hold out for the right one. sfil14: inc ax ; Increment the packet number. and ax,3FH ; Turn off the two high order bits. mov pack.pktnum,ax ; Save modulo 64 of the number. inc pack.numpkt ; Increment the number of packets. mov ah,pack.numtry ; Get the number of tries. mov pack.oldtry,ah ; Save it. mov pack.numtry,0 ; Reset the number of tries. sfil15: mov ah,0 ; Get a zero. mov bx,offset fcb add bx,20H mov [bx],ah ; Set the record number to zero. ; mov flags.eoflag,ah ; Indicate not EOF. (Done in GETFIL). mov ah,0FFH mov flags.filflg,ah ; Indicate file buffer empty. call gtchr jmp sfil16 ; Error go see if its EOF. nop jmp sfil17 ; Got the chars, proceed. sfil16: cmp ah,0FFH ; Is it EOF? je sfl161 jmp abort ; If not give up. sfl161: mov ah,'Z' ; Set the state to EOF. mov pack.state,ah ret sfil17: mov siz,ax mov pack.state,'D' ; Set the state to data send. ret sfile2: cmp ah,'N' ; NAK? jne sfile3 ; Try if error packet. call rtpos ; Position cursor. inc pack.numrtr ; Increment the number of retries mov ax,pack.numrtr call nout ; Write the number of retries. mov ax,pack.pktnum ; Get the present packet number. inc ax ; Increment. and ax,03FH ; Account for wraparound. [18] cmp ax,pack.argblk ; Is the packet's number one more than now? jz sfil14 ; Just as good as a ACK; go to the ACK code. ret ; If not go try again. sfile3: cmp ah,'E' ; Is it an error packet. jne sfile4 call error sfile4: jmp abort SFILE ENDP ; Send data SDATA PROC NEAR cmp flags.cxzflg,0 ; Have we seen ^X or ^Z? je sdata2 ; Nope, just continue. cmp flags.cxzflg,'C' ; Stop it all? [25] jne sdata1 ; It was a ^X or ^Z. mov pack.state,'A' ; It was a ^C -- abort [25] ret sdata1: mov pack.state,'Z' ; Else, abort sending the file. ret sdata2: cmp pack.numtry,maxtry ; Have we reached the maximum number of tries? jl sdata3 call erpos mov dx,offset erms14 mov ah,prstr int dos ; Print an error message. mov bx,offset erms22 call errpack ; Send error packet just in case. jmp abort ; Change the state to abort. sdata3: inc pack.numtry ; Increment it. mov datptr,offset data ; Get a pointer to our data block. mov chrptr,offset filbuf ; Pointer to chars to be sent. mov cx,siz ; number to transfer mov si,chrptr ; source of characters mov di,datptr ; destination cmp flags.eofcz,0 ; stopping on ctl-z's? jz sdata6 ; no, do blind copy sdata4: lodsb ; get a byte cmp al,'Z'-40H ; is it a ctl-z? je sdata5 ; yes, break loop stosb ; else copy it loop sdata4 ; and keep going sdata5: mov ax,siz ; size to send sub ax,cx ; minus actually sent... jmp short sdata7 sdata6: rep movsb ; just copy data mov ax,siz ; this is how many were moved sdata7: mov pack.argbk1,ax mov ax,pack.pktnum ; Get the packet number. mov pack.argblk,ax mov ah,'D' ; Data packet. call spack ; Send the packet. jmp tryagn ; if can't send it, retry before giving up call rpack ; Get a packet. jmp tryagn ; Trashed packet don't change state, retry. call dodec ; Do all decoding. cmp ah,'Y' ; ACK? jne sdat14 ; If not try next. mov ax,pack.pktnum ; Get the packet number. cmp ax,pack.argblk ; Is it the right packet number? jz sdata8 ret ; If not hold out for the right one. sdata8: inc ax ; Increment the packet number. and ax,3FH ; Turn off the two high order bits. mov pack.pktnum,ax ; Save modulo 64 of the number. inc pack.numpkt ; Increment the number of packets. mov ah,pack.numtry ; Get the number of tries. mov pack.oldtry,ah ; Save it. mov pack.numtry,0 ; Reset the number of tries. cmp pack.argbk1,1 ; Does the ACK contain data? jne sdat11 ; Nope, so continue. mov bx,offset data ; If yes, check the data field. mov ah,[bx] ; Pick it up. cmp ah,'X' ; Other side requests ^X? jne sdata9 ; Nope. jmp sdat10 ; And leave. sdata9: cmp ah,'Z' ; Other side requests ^Z? jne sdat11 ; Nope. sdat10: mov flags.cxzflg,ah ; Yes remember it. mov pack.state,'Z' ; Abort sending file(s). ret sdat11: call gtchr jmp sdat12 ; Error go see if its EOF. mov siz,ax ; Save the size of the data gotten. ret sdat12: cmp ah,0FFH ; Is it EOF? je sdat13 jmp abort ; If not give up. sdat13: mov pack.state,'Z' ; Set the state to EOF. ret sdat14: cmp ah,'N' ; NAK? jne sdat15 ; See if is an error packet. call rtpos ; Position cursor. inc pack.numrtr ; Increment the number of retries mov ax,pack.numrtr call nout ; Write the number of retries. mov ax,pack.pktnum ; Get the present packet number. inc ax ; Increment. and ax,03FH ; Account for wraparound. [18] cmp ax,pack.argblk ; Is the packet's number one more than now? jz sdata8 ; Just as good as ACK; goto ACK code. ret ; If not go try again. sdat15: cmp ah,'E' ; Is it an error packet. jne sdat16 call error sdat16: jmp abort SDATA ENDP ; Send EOF SEOF PROC NEAR cmp pack.numtry,maxtry ; Have we reached the maximum number of tries? jl seof1 call erpos ; Position cursor. mov dx,offset erms14 mov ah,prstr int dos ; Print an error message. mov bx,offset erms23 call errpack ; Send error packet just in case. jmp abort ; Change the state to abort. seof1: inc pack.numtry ; Increment it. mov ax,pack.pktnum ; Get the packet number. mov pack.argblk,ax mov pack.argbk1,0 ; No data. cmp flags.cxzflg,0 ; Seen a ^X or ^Z? je seof11 ; Nope, send normal EOF packet. mov bx,offset data ; Get data area of packet. mov ah,'D' ; Use "D" for discard. mov [bx],ah ; And add it to the packet. mov pack.argbk1,1 ; Set data size to 1. seof11: mov cx,pack.argbk1 ; Put size in CX. call doenc ; Encode the packet. mov ah,'Z' ; EOF packet. call spack ; Send the packet. jmp abort call rpack ; Get a packet. jmp tryagn ; Trashed packet don't change state, retry. call dodec ; Do decoding. cmp ah,'Y' ; ACK? jne seof2 ; If not try next. mov ax,pack.pktnum ; Get the packet number. cmp ax,pack.argblk ; Is it the right packet number? jz seof12 ret ; If not hold out for the right one. seof12: inc ax ; Increment the packet number. and ax,3FH ; Turn off the two high order bits. mov pack.pktnum,ax ; Save modulo 64 of the number. inc pack.numpkt ; Increment the number of packets. mov ah,pack.numtry ; Get the number of tries. mov pack.oldtry,ah ; Save it. mov pack.numtry,0 ; Reset the number of tries. mov ah,closf ; Close the file. mov dx,offset fcb int dos call gtnfil ; Get the next file. jmp seof13 ; No more. mov pack.state,'F' ; Set the state to file send. cmp flags.cxzflg,'X' ; Control-X seen? jne seof14 call cxmsg ; Clear out the interrupt msg. seof14: mov flags.cxzflg,0 ; Reset the flag. ret seof13: mov pack.state,'B' ; Set the state to EOT. ret seof2: cmp ah,'N' ; NAK? jne seof3 ; Try and see if its an error packet. call rtpos ; Position cursor. inc pack.numrtr ; Increment the number of retries mov ax,pack.numrtr call nout ; Write the number of retries. mov ax,pack.pktnum ; Get the present packet number. inc ax ; Increment. and ax,03FH ; Account for wraparound. [18] cmp ax,pack.argblk ; Is the packet's number one more than now? jz seof12 ; Just as good as a ACK; go to the ACK code. ret ; If not go try again. seof3: cmp ah,'E' ; Is it an error packet? jne seof4 call error seof4: jmp abort SEOF ENDP ; Send EOT SEOT PROC NEAR cmp pack.numtry,maxtry ; Have we reached the maximum number of tries? jl seot1 call erpos ; Position cursor. mov dx,offset erms14 mov ah,prstr int dos ; Print an error message. mov bx,offset erms24 call errpack ; Send error packet just in case. jmp abort ; Change the state to abort. seot1: inc pack.numtry ; Increment it. mov ax,pack.pktnum ; Get the packet number. mov pack.argblk,ax mov pack.argbk1,0 ; No data. mov cx,pack.argbk1 call doenc ; Encode packet. mov ah,'B' ; EOF packet. call spack ; Send the packet. jmp abort call rpack ; Get a packet. jmp tryagn ; Trashed packet don't change state, retry. call dodec ; Decode packet. cmp ah,'Y' ; ACK? jne seot2 ; If not try next. mov ax,pack.pktnum ; Get the packet number. cmp ax,pack.argblk ; Is it the right packet number? jz seot12 ret ; If not hold out for the right one. seot12: inc ax ; Increment the packet number. and ax,3FH ; Turn off the two high order bits. mov pack.pktnum,ax ; Save modulo 64 of the number. inc pack.numpkt ; Increment the number of packets. mov ah,pack.numtry ; Get the number of tries. mov pack.oldtry,ah ; Save it. mov pack.numtry,0 ; Reset the number of tries. mov pack.state,'C' ; Set the state to file send. ret seot2: cmp ah,'N' ; NAK? jne seot3 ; Is it error. call rtpos ; Position cursor. inc pack.numrtr ; Increment the number of retries mov ax,pack.numrtr call nout ; Write the number of retries. mov ax,pack.pktnum ; Get the present packet number. inc ax ; Increment. and ax,03FH ; Account for wraparound. [18] cmp ax,pack.argblk ; Is the packet's number one more than now? jz seot12 ; Just as good as a ACK; go to the ACK code. ret ; If not go try again. seot3: cmp ah,'E' ; Is it an error packet. jne seot4 call error seot4: jmp abort SEOT ENDP tryagn: call updrtr ret newfn: mov ah,prstr mov dx,offset asmsg int dos mov ah,dconio mov si,offset sendas ; Buffer where the name is. mov di,offset data mov ch,0 mov cl,difsiz ; Length of name. newf0: lodsb ; Get a char. cmp al,61H jb newf1 ; Leave alone if less than 'a'? cmp al,7AH ja newf1 ; Leave alone if over 'z'. sub al,20H ; Uppercase the letters. newf1: stosb mov dl,al cmp flags.remflg,0 ; should we print? jne newf2 ; no, we're in remote mode. int dos ; Print them. newf2: loop newf0 mov ch,0 mov cl,difsiz ; Reset the length field. ret ; Do encoding. Expectx CX to be the data size. doenc: jcxz doen0 mov chrcnt,cx ; Number of chars in filename. mov bx,offset data ; Source of data. mov bufpnt,bx mov bx,offset nulref ; Null routine for refilling buffer. mov ah,rptq mov origr,ah ; Save repeat prefix here. mov rptct,1 ; Number of times char is repeated. mov rptval,0 ; Value of repeated char. call encode ; Make a packet with size in AX. nop nop nop mov pack.argbk1,ax ; Save number of char in filename. mov cx,ax call movpak ; Move to data part of packet. doen0: ret ; CX is set before this is called. movpak: push es mov ax,ds mov es,ax mov si,offset filbuf ; Move from here mov di,offset data ; to here repne movsb pop es ret ; Do decoding. dodec: cmp pack.argbk1,0 je dodc0 push ax ; Save packet size. mov cx,pack.argbk1 ; Size of data. mov bx,offset data ; Address of data. mov ax,offset nulr ; Routine to dump buffer (null routine). mov bufpnt,offset decbuf ; Where to put output. mov chrcnt,80H ; Buffer size. call decode nop nop nop call decmov ; Move decoded data back to "data" buffer. pop ax dodc0: ret ; Move decoded data from decode buffer back to "data". decmov: push si push di push es mov ax,ds mov es,ax mov cx,bufpnt ; Last char we added. sub cx,offset decbuf ; Get actual number of characters. mov pack.argbk1,cx ; Remember size of real data. lea si,decbuf ; Data is here. lea di,data ; Move to here. repne movsb ; Copy the data. pop es pop di pop si ret ; Abort ABORT PROC NEAR mov pack.state,'A' ; Otherwise abort. ret ABORT ENDP ; This is where we go if we get an error packet. A call to ERROR ; positions the cursor and prints the message. A call to ERROR1 ; just prints a CRLF and then the message. [8] ERROR PROC NEAR mov pack.state,'A' ; Set the state to abort. call erpos ; Position the cursor. jmp error2 error1: mov ah,prstr mov dx,offset crlf int dos error2: mov bx,pack.argbk1 ; Get the length of the data. add bx,offset data ; Get to the end of the string. mov ah,'$' ; Put a dollar sign at the end. mov [bx],ah mov ah,prstr ; Print the error message. mov dx,offset data int dos ret ERROR ENDP ; Set the maximum data packet size. [21b] PACKLEN PROC NEAR mov ah,trans.spsiz ; Maximum send packet size. sub ah,4 ; Size minus control info. sub ah,trans.chklen ; And minus checksum chars. sub ah,2 ; Leave room at end: 2 for possible #X. cmp trans.ebquot,'N' ; Doing 8-bit quoting? je pack0 ; Nope so we've got our size. cmp trans.ebquot,'Y' je pack0 ; Not doing it in this case either. sub ah,1 ; Another 1 for 8th-bit quoting. pack0: cmp rptq,0 ; Doing repeat character quoting? je pack1 ; Nope, so that's all for now. sub ah,2 ; Another 2 for repeat prefix. pack1: mov trans.maxdat,ah ; Save max length for data field. ret PACKLEN ENDP ; Print the number in AX on the screen in decimal rather that hex. [19a] NOUT PROC NEAR cmp flags.xflg,1 ; Writing to screen? [21c] je nout1 ; Yes, just leave. [21c] push ax push dx mov temp,10 ; Divide quotient by 10. ; cwd ; Convert word to doubleword. mov dx,0 ; High order word should be zero. div temp ; AX <-- Quo, DX <-- Rem. cmp ax,0 ; Are we done? jz nout0 ; Yes. call nout ; If not, then recurse. nout0: add dl,'0' ; Make it printable. mov temp,ax mov ah,conout int dos mov ax,temp pop dx pop ax nout1: ret ; We're done. [21c] NOUT 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 //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 640 mssend.asm /bin/echo -n ' '; /bin/ls -ld mssend.asm fi /bin/echo 'Extracting msserv.asm' sed 's/^X//' <<'//go.sysin dd *' >msserv.asm public logout, bye, finish, remote, get, server include msdefs.h datas segment public 'datas' extrn data:byte, flags:byte, trans:byte, pack:byte, curchk:byte extrn fcb:byte remcmd db 0 ; Remote command to be executed. [21c] rempac db 0 ; Packet type: C (host) or G (generic). [21c] cmer05 db cr,lf,'?Filename must be specified$' ; [21a] ermes7 db '?Unable to receive initiate$' erms18 db cr,lf,'?Unable to tell host that session is finished$' erms19 db cr,lf,'?Unable to tell host to logout$' erms21 db cr,lf,'?Unable to tell host to execute command$' ; [21c] infms1 db 'Entering server mode',cr,lf,'$' remms1 db 'Kermit-MS: Unknown server command$' remms2 db 'Kermit-MS: Illegal file name$' remms3 db 'Kermit-MS: Unknown generic command$' pass db lf,cr,' Password: $' ; When change directory. [21c] crlf db cr,lf,'$' tmp db ?,'$' temp dw 0 oloc dw 0 ; Original buffer location. [21c] osiz dw 0 ; Original buffer size. [21c] inpbuf dw 0 ; Pointer to input buffer. [21c] cnt dw 0 delinp db BS,BS,BS,' ',BS,BS,BS,'$' ; When DEL key is used. [21d] clrspc db ' ',10O,'$' ; Clear space. srvchr db 'SRGIE' ; server cmd characters srvfln equ $-srvchr ; length of tbl srvfun dw srvsnd,srvrcv,srvgen,srvini,serv1 remhlp db cr,lf,'CWD connect to a directory' ; [21c start] db cr,lf,'DELETE a file' db cr,lf,'DIRECTORY listing' db cr,lf,'HELP' db cr,lf,'HOST command' db cr,lf,'SPACE in a directory' db cr,lf,'TYPE a file$' ; [21c end] remtab db 07H ; Seven entries. [21c start] mkeyw 'CWD',remcwd mkeyw 'DELETE',remdel mkeyw 'DIRECTORY',remdir mkeyw 'HELP',remhel mkeyw 'HOST',remhos mkeyw 'SPACE',remdis mkeyw 'TYPE',remtyp ; [21c end] remfnm db ' Remote Source File: $' lclfnm db ' Local Destination File: $' filhlp db ' File name to receive as$' filmsg db ' Remote file specification or confirm with carriage return $' frem db ' Name of file on remote system $' genmsg db ' Enter text to be sent to remote server $' rdbuf db 80H DUP(?) datas ends code segment public extrn comnd:near, serrst:near, spack:near, rpack5:near, init:near extrn read12:near, serini:near, read2:near, rpar:near, spar:near extrn rin21:near, rfile3:near, error1:near, clrfln:near extrn dodel:near, clearl:near, dodec: near, doenc:near extrn packlen:near, send11:near, errpack:near, init1:near extrn rpack:near,nak:near, rrinit:near, cmblnk:near extrn error:near, erpos:near, rprpos:near, clrmod:near extrn prompt:near assume cs:code,ds:datas ; LOGOUT - tell remote KERSRV to logout. LOGOUT PROC NEAR mov ah,cmcfm call comnd ; Get a confirm. jmp r call logo jmp rskp ; Go get another command whether we .... jmp rskp ; .... succeed or fail. LOGOUT ENDP LOGO PROC NEAR mov pack.numtry,0 ; Initialize count. mov pack.numrtr,0 ; No retries yet. call serini ; Initialize port. [14] mov ah,trans.chklen ; Don't forget the checksum length. mov curchk,ah mov trans.chklen,1 ; Use one char for server functions. logo1: cmp pack.state,'A' ; Did user type a ^C? je logo2x ; Yes just leave. mov ah,pack.numtry cmp ah,maxtry ; Too many times? js logo3 ; No, try it. logo2: mov ah,prstr mov dx,offset erms19 int dos logo2x: call serrst ; Reset port. [14] mov ah,curchk mov trans.chklen,ah ; Restore value. ret logo3: inc pack.numtry ; Increment number of tries. mov pack.argblk,0 ; Packet number zero. mov pack.argbk1,1 ; One piece of data. mov bx,offset data mov ah,'L' mov [bx],ah ; Logout the remote host. mov cx,1 ; One piece of data. call doenc ; Do encoding. mov ah,'G' ; Generic command packet. call spack jmp logo2 ; Tell user and die. nop call rpack5 ; Get ACK (w/o screen msgs.) jmp logo1 ; Go try again. nop push ax call dodec ; Decode packet. mov ah,curchk mov trans.chklen,ah ; Restore value. pop ax cmp ah,'Y' ; ACK? jne logo4 call serrst ; Reset port. [14] jmp rskp logo4: cmp ah,'E' ; Error packet? jnz logo1 ; Try sending the packet again. call error1 call serrst ; Reset port. [14] ret LOGO ENDP ; FINISH - tell remote KERSRV to exit. FINISH PROC NEAR mov ah,cmcfm ; Parse a confirm. call comnd jmp r mov pack.numtry,0 ; Initialize count. mov pack.numrtr,0 ; No retries yet. call serini ; Initialize port. [14] mov ah,trans.chklen ; Don't forget the checksum length. mov curchk,ah mov trans.chklen,1 ; Use one char for server functions. fin1: cmp pack.state,'A' ; ^C typed? je fin2x mov ah,pack.numtry cmp ah,maxtry ; Too many times? js fin3 ; Nope, try it. fin2: mov ah,prstr mov dx,offset erms18 int dos fin2x: call serrst ; Reset port. [14] mov ah,curchk mov trans.chklen,ah ; Restore value. jmp rskp ; Go home. fin3: inc pack.numtry ; Increment number of tries. mov pack.argblk,0 ; Packet number zero. mov pack.argbk1,1 ; One piece of data. mov bx,offset data mov ah,'F' mov [bx],ah ; Finish running Kermit. mov cx,1 ; One piece of data. call doenc ; Do encoding. mov ah,'G' ; Generic command packet. call spack jmp fin2 ; Tell user and die. nop call rpack5 ; Get ACK (w/o screen stuff). jmp fin1 ; Go try again. nop push ax call dodec ; Decode data. mov ah,curchk mov trans.chklen,ah ; Restore value. pop ax cmp ah,'Y' ; Got an ACK? jnz fin4 call serrst ; Reset port. [14] jmp rskp ; Yes, then we're done. fin4: cmp ah,'E' ; Error packet? jnz fin1 ; Try sending it again. call error1 call serrst ; Reset port. [14] jmp rskp FINISH ENDP ; BYE command - tell remote KERSRV to logout & exits to DOS. BYE PROC NEAR mov ah,cmcfm ; Parse a confirm. call comnd jmp r call logo ; Tell the mainframe to logout. jmp rskp ; Failed - don't exit. mov flags.extflg,1 ; Set exit flag. jmp rskp ; [8 end] BYE ENDP ; Tell remote server to send the specified file(s). get PROC NEAR mov flags.droflg,0 ; Reset flags from fn parsing. mov flags.nmoflg,0 ; Reset flags from fn parsing. mov flags.cxzflg,0 ; no ctl-c typed yet... mov bx,offset data ; Where to put text. [8 start] mov dx,offset filmsg ; In case user needs help. mov ah,cmtxt call comnd ; Get text or confirm. jmp r ; Fail. cmp ah,0 ; Read in any chars? jne get4 ; Yes, then OK. ; empty line, ask for file names get1: mov dx,offset remfnm ; ask for remote first call prompt mov bx,offset data mov dx,offset frem mov ah,cmtxt call comnd ; get a line of text jmp r cmp flags.cxzflg,'C' ; ctl-C typed? jne get2 ; no, continue jmp rskp get2: cmp ah,0 je get1 ; ignore empty lines mov bl,ah mov bh,0 mov byte ptr data[bx],'$' ; terminate name for printing mov pack.argbk1,bx ; remember length here mov dx,offset lclfnm call prompt mov ah,cmifi mov bx,offset filhlp mov dx,offset fcb call comnd jmp r mov ah,cmcfm call comnd jmp r cmp flags.cxzflg,'C' ; control-C typed? jne get3 ; no, keep going jmp rskp get3: mov flags.nmoflg,1 ; remember changed name jmp short get5 get4: mov al,ah mov ah,0 mov pack.argbk1,ax ; Remember number of chars we read. mov byte ptr [bx],'$' ; use for printing. get5: cmp flags.remflg,0 ; remote mode? jne get6 ; yes, don't print anything call init ; Clear line and initialize buffers. call clrfln ; Prepare to print filename. mov ah,prstr mov dx,offset data ; Print file name. int dos get6: call init1 ; init buffers mov pack.numtry,0 ; Initialize count. mov pack.numrtr,0 ; No retries yet. mov pack.state,'R' ; this is what state will soon be... call serini ; Initialize port. mov cx,pack.argbk1 ; Data size. call doenc ; Encode data. mov ah,trans.chklen ; Don't forget the checksum length. mov curchk,ah mov trans.chklen,1 ; Use one char for server functions. get7: cmp pack.state,'A' ; Did user type a ^C? je get9 ; Yes - just return to main loop. mov ah,pack.numtry cmp ah,maxtry ; Too many times? jbe get10 ; Nope, try it. get8: cmp flags.remflg,0 ; remote mode? jne get9 ; yes, no printing call erpos mov ah,prstr mov dx,offset ermes7 ; Can't get init packet. int dos get9: call serrst ; Reset port. mov ah,curchk mov trans.chklen,ah ; Restore value. jmp rskp ; Go home. get10: inc pack.numtry ; Increment number of tries. mov pack.argblk,0 ; Start at packet zero. mov ah,'R' ; Receive init packet. call spack ; Send the packet. jmp get8 ; Tell user we can't do it. nop call rpack5 ; Get ACK (w/o screen stuff). jmp get7 ; Got a NAK - try again. nop push ax mov ah,curchk mov trans.chklen,ah ; Restore value. pop ax mov pack.argbk2,ax ; this is where rinit wants pkt type if getting mov flags.getflg,1 ; "Get" as vs "Receive". jmp read12 ; go join read code get11: mov ah,prstr ; Complain if no filename. mov dx,offset cmer05 int dos jmp rskp GET ENDP ; server command server proc near mov ah,cmcfm call comnd jmp r push es mov ax,ds mov es,ax ; address data segment mov al,flags.remflg ; get remote flag push ax ; preserve for later mov flags.remflg,1 ; set remote if server call cmblnk ; clear screen mov ah,prstr mov dx,offset infms1 int dos ; should reset to default parms here... ; should increase timeout interval serv1: call serini ; init serial line (send & recv reset it) mov trans.chklen,1 ; checksum len = 1 mov pack.pktnum,0 ; pack number resets to 0 mov pack.numtry,0 ; no retries yet. call rpack ; get a packet jmp short serv2 ; no good, nak and continue nop jmp short serv3 ; try to figure this out serv2: cmp flags.cxzflg,'C' ; ctl-C? je serv5 ; yes, stop this. call nak ; nak the packet jmp serv1 ; and keep readiserv2 packets serv3: mov di,offset srvchr ; server characters mov cx,srvfln ; length of striserv2 mov al,ah ; packet type repne scasb ; hunt for it je serv4 ; we know this one, go handle it mov bx,offset remms1 ; else give a message call errpack ; back to local kermit jmp serv1 ; and keep lookiserv2 for a cmd serv4: sub di,offset srvchr+1 ; find offset, +1 for pre-increment shl di,1 ; convert to word index. call srvfun[di] ; call the appropriate handler jmp serv5 ; someone wanted to exit... ; should we reset serial line? jmp serv1 ; else keep goiserv2 for more cmds. serv5: ;** restore timer values pop ax ; get this off stack mov flags.remflg,al ; restore old flag call serrst ; reset serial handler pop es ; restore register jmp rskp ; and return server endp ; server commands. ; srvsnd - receives a file that the local kermit is sending. srvsnd proc near mov bx,offset data call spar ; parse the send-init packet call packlen ; figure max packet mov bx,offset data call rpar ; make answer for them mov al,ah ; length of packet mov ah,0 mov pack.argbk1,ax ; store length for spack mov ah,'Y' ; ack call spack ; answer them jmp rskp ; can't answer, forget this call rrinit ; init variables for init inc pack.pktnum ; count the send-init packet. mov pack.state,'F' ; expecting file name about now call read2 ; and join read code nop nop nop ; ignore errors jmp rskp ; and return for more srvsnd endp ; srvrcv - send a file that they're receiving. srvrcv proc near mov si,offset data ; this should be filename mov di,offset fcb ; this is where filename goes mov al,1 ; skip leading separators mov ah,prsfcb ; parse an fcb int dos ; let dos do the work cmp al,0ffh ; invalid? jne srvrc1 ; no, keep going mov bx,offset remms2 ; complain call errpack ; that we can't find it jmp rskp ; and return srvrc1: mov pack.state,'R' ; remember state. call send11 ; this should send it jmp rskp jmp rskp ; return in any case srvrcv endp ; srvgen - generic server commands. ; We only support Logout and Finish right now. srvgen proc near mov al,data ; get 1st packet char cmp al,'F' ; maybe finish? je srvge1 ; yup, handle cmp al,'L' ; logout? jne srvge2 ; no. srvge1: mov pack.argbk1,0 ; 0-length data mov ah,'Y' call spack ; ack it nop nop nop ; *** ignore error? ret ; and return to signal exit. srvge2: mov bx,offset remms3 call errpack jmp rskp srvgen endp ; srvini - init parms based on init packet srvini proc near mov bx,offset data call spar ; parse info call packlen ; this should really be part of spar, but... mov bx,offset data call rpar ; get receive info mov al,ah mov ah,0 mov pack.argbk1,ax ; set size of return info mov ah,'Y' call spack ; send the packet off jmp rskp jmp rskp ; and go succeed srvini endp ; This is the REMOTE command. [21c] REMOTE PROC NEAR mov dx,offset remtab ; Parse a keyword from the REMOTE table. mov bx,offset remhlp mov ah,cmkey call comnd jmp r call bx ; Call the appropriate routine. jmp r ; Command failed. jmp rskp REMOTE ENDP ; REMDIS - Get disk usage on remote system. [21c] REMDIS PROC NEAR mov remcmd,'U' ; Disk usage command. mov rempac,'G' ; Packet type = generic. jmp genric ; Execute generic Kermit command. REMDIS ENDP ; REMHEL - Get help about remote commands. [21c] REMHEL PROC NEAR mov remcmd,'H' ; Help...... mov rempac,'G' ; Packet type = generic. jmp genric ; Execute generic Kermit command. REMHEL ENDP ; REMTYP - Print a remote file. [21c] REMTYP PROC NEAR mov remcmd,'T' ; Type the file. mov rempac,'G' ; Packet type = generic. jmp genric REMTYP ENDP ; REMHOS - Execute a remote host command. [21c] REMHOS PROC NEAR mov remcmd,' ' ; Don't need one. mov rempac,'C' ; Packet type = remote command. jmp genric REMHOS ENDP ; REMDIR - Do a directory. [21c] REMDIR PROC NEAR mov remcmd,'D' mov rempac,'G' ; Packet type = generic. jmp genric REMDIR ENDP ; REMDEL - Delete a remote file. [21c] REMDEL PROC NEAR mov remcmd,'E' mov rempac,'G' ; Packet type = generic. jmp genric REMDEL ENDP ; REMCWD - Change remote working directory. [21c] REMCWD PROC NEAR mov remcmd,'C' mov rempac,'G' ; Packet type = generic. jmp genric REMCWD ENDP ; GENRIC - Send a generic command to a remote Kermit server. [21c] GENRIC PROC NEAR mov bx,offset rdbuf ; Where to put the text. cmp rempac,'C' ; Remote host command? je genra ; Yes, leave as is. add bx,2 ; Leave room for type and size. genra: mov ah,cmtxt ; Parse arbitrary text up to a CR. mov dx,offset genmsg ; In case they want text. call comnd jmp r mov al,ah ; Don't forget the size. mov ah,0 mov cnt,ax ; Save it here. cmp rempac,'C' ; Remote host command? jne genrb ; No, skip this part. call ipack jmp genr2 mov pack.numtry,0 mov ah,trans.chklen mov curchk,ah ; Save desired checksum length. mov trans.chklen,1 ; Use 1 char for server functions. mov pack.numrtr,0 ; No retries yet. jmp genr1 ; Send the packet. genrb: mov ax,cnt cmp ax,0 ; Any data? je genr0 ; Nope. mov ah,al ; Don't overwrite the real count value. add ah,32 ; Do the char function. mov temp,bx ; Remember where we are. mov bx,offset rdbuf+1 ; Size of remote command. mov [bx],ah mov ah,0 inc al ; For the size field. cmp remcmd,'C' ; Change working directory? jne genr0 ; No, so don't ask for password. mov cnt,ax ; Save here for a bit. mov ah,prstr mov dx,offset pass ; Send along an optional password. int dos mov bx,temp ; Where to put the password. push bx ; Is safe since subroutine never fails. inc bx ; Leave room for count field. call input ; Read in the password. mov temp,bx ; Remember end of data pointer. pop bx ; Where to put the size. cmp ah,0 ; No password given? jne genrc mov ax,cnt jmp genr0 ; Then that's it. genrc: mov al,ah add ah,32 ; Make it printable. mov [bx],ah ; Tell remote host the size. mov ah,0 push ax ; Remember the count. call clearl ; Clear to end-of-line. pop ax inc al ; For second count value. add ax,cnt ; Total for both fields of input. genr0: inc al ; For the char representing the command. mov pack.argbk1,ax ; Set the size. mov cnt,ax ; And remember it. mov pack.numtry,0 ; Initialize count mov bx,offset rdbuf ; Start of data buffer. mov ah,remcmd ; Command subtype. mov [bx],ah call ipack ; Send init parameters. jmp genr2 nop ; Make it 3 bytes long. mov ah,trans.chklen mov curchk,ah ; Save desired checksum length. mov trans.chklen,1 ; Use 1 char for server functions. mov pack.numrtr,0 ; No retries yet. genr1: cmp pack.state,'A' ; Did the user type a ^C? je genr2x mov ah,pack.numtry cmp ah,maxtry ; Too many tries? js genr3 ; Nope, keep trying. genr2: mov ah,prstr mov dx,offset erms21 ; Print error msg and fail. int dos genr2x: call serrst ; Reset the port. mov ah,curchk mov trans.chklen,ah ; Restore. jmp rskp genr3: push es ; Prepare to put string into packet. mov ax,ds mov es,ax mov si,offset rdbuf ; Move from here mov di,offset data ; to here. mov cx,cnt ; Move this many characters. rep movsb ; Perform the string move. pop es mov ax,cnt mov pack.argbk1,ax ; How much data to send. mov cx,ax ; Size of data. call doenc ; Encode it. inc pack.numtry ; Increment number of trials. mov pack.argblk,0 ; Packet number 0. mov ah,rempac ; Packet type. call spack ; Send the packet. jmp genr2 ; Tell user we can't do it. nop call rpack5 ; Get ACK (w/o screen stuff) jmp genr1 ; Got a NAK - try again. nop push ax mov ah,curchk mov trans.chklen,ah ; Restore. pop ax cmp ah,'Y' ; Is all OK? jne genr4 cmp pack.argbk1,0 ; Any data in the ACK? je genr31 ; Nope - just return. call dodec ; Decode data. mov ah,prstr mov dx,offset crlf ; First go to a new line. int dos mov di,offset data ; Where the reply is. mov cx,pack.argbk1 ; How much data we have. call prtscr ; Print it on the screen. genr31: jmp rskp ; And we're done. genr4: cmp ah,'X' ; Text packet? je genr5 cmp ah,'S' ; Handling this like a file? jne genr6 mov pack.state,'R' ; Set the state. mov bx,offset rin21 ; Where to go to. jmp genr51 ; Continue. genr5: mov pack.state,'F' call dodec ; Decode data. mov bx,offset rfile3 ; Jump to here. genr51: mov tmp,ah ; Save packet type. mov flags.xflg,1 ; Remember we saw an "X" packet. mov pack.numtry,0 mov pack.numrtr,0 mov pack.numpkt,0 mov pack.pktnum,0 mov flags.cxzflg,0 mov ah,tmp ; Packet type. call bx ; Handle it almost like filename. call read2 ; Receive the rest. jmp r ; Oops, we failed. jmp rskp ; Done OK. genr6: cmp ah,'E' ; Error packet? je genr6x jmp genr1 ; Try again. genr6x: call dodec ; Decode data. call error1 ; Print the error messge. call serrst jmp rskp ; And return. GENRIC ENDP ; Send "I" packet with transmission parameters. [21c] IPACK PROC NEAR mov ah,trans.chklen mov curchk,ah ; Initialize. call serini mov pack.pktnum,0 ; Use packet number 0. mov pack.numtry,0 ; Number of retries. ipk0: cmp pack.state,'A' ; Did user type a ^C? je ipk0x cmp pack.numtry,imxtry ; Reached our limit? jl ipk1 ipk0x: ret ; Yes, so we fail. ipk1: inc pack.numtry ; Save the updated number of tries. mov bx,offset data ; Get a pointer to our data block. call rpar ; Set up the parameter information. xchg ah,al mov ah,0 mov pack.argbk1,ax ; Save the number of arguments. mov pack.argblk,0 ; Use packet number 0. mov ah,trans.chklen mov curchk,ah ; Save real value. mov trans.chklen,1 ; One char for server function. mov ah,'I' ; "I" packet. call spack ; Send the packet. jmp ipk4 nop call rpack5 ; Get a packet. jmp ipk4 ; Try again. nop push ax mov ah,curchk mov trans.chklen,ah ; Reset. pop ax cmp ah,'Y' ; ACK? jne ipk3 ; If not try next. mov ax,pack.pktnum ; Get the packet number. cmp ax,pack.argblk ; Is it the right packet number? je ipk2 jmp ipk0 ; If not try again. ipk2: mov ax,pack.argbk1 ; Get the number of pieces of data. mov bx,offset data ; Pointer to the data. call spar ; Read in the data. mov ah,trans.chklen mov curchk,ah ; This is what we decided on. call packlen ; Get max send packet size. [21b] mov pack.numtry,0 ; Reset the number of tries. jmp rskp ipk3: cmp ah,'N' ; NAK? je ipk0 ; Yes, try again. cmp ah,'E' ; Is it an error packet. je ipk3x jmp ipk0 ; Trashed data. ipk3x: jmp rskp ; Other side doesn't know about "I" packet. ipk4: mov ah,curchk mov trans.chklen,ah ; Reset. jmp ipk0 ; Keep trying. IPACK ENDP ; Returns in AH the count of characters read in. ; in BX the updated pointer to the input buffer. INPUT PROC NEAR mov cl,0 ; Keep a count. mov inpbuf,bx ; Where to put data. input0: mov ah,conin ; Read in a char. int dos cmp al,CR ; Done with input? jne input1 mov ah,cl ; Return count in AH. jmp r input1: cmp al,BS ; Backspace? je inpt11 ; cmp al,DEL ; Or delete? jne input3 call dodel ; Erase weird character. inpt11: dec cl ; Don't include in char count. cmp cl,0 ; Backspaced too much? jns input2 ; No, is OK. push bx call clearl pop bx mov ah,conout mov dl,bell int dos mov cl,0 jmp input0 input2: dec bx ; 'Remove' from buffer. mov ah,prstr mov dx,offset clrspc int dos jmp input0 ; Go get more. input3: cmp al,'U'-64 ; Control-U? jne input4 mov ah,prstr mov dx,offset pass+1 int dos push bx push cx call clearl ; Blank out the line. pop cx pop bx mov cl,0 ; Reset count to zero. mov bx,inpbuf ; Start at head of buffer. jmp input0 input4: cmp al,0 ; Two character sequence? jne input5 mov ah,conin int dos ; Get second char. cmp al,83 ; Delete key? je inpt40 ; Yup. cmp al,75 ; Backarrow key? je inpt40 call dodel ; Erase weird character. jmp input0 ; And go on computing. inpt40: mov ah,prstr mov dx,offset delinp ; Erase weird character. int dos jmp inpt11 ; Remove the offending char. input5: mov [bx],al ; Add char to buffer. inc cl ; Include in count. inc bx jmp input0 INPUT ENDP ; Print data onto the screen. If text has no "$" in it, just print ; it. Else, do special output for the "$". ; Routine expects: DI = Start of buffer we are to print. ; CX = Number of characters to print. [21c] PRTSCR PROC NEAR mov al,'$' ; This is what we're looking for. mov oloc,di ; Remember original buffer address. mov osiz,cx ; And original size. push es mov bx,ds mov es,bx ; Have ES point to data area. prts0: repnz scasb ; Search for "$" in the buffer. cmp cx,0 ; Found one? je prts1 ; No, do a regular DOS call. mov ah,prstr mov dx,oloc ; Print up to the "$". int dos mov ah,dconio mov dl,'$' int dos ; Print the "$" mov oloc,di ; New starting location. mov osiz,cx ; New size. jmp prts0 prts1: mov bx,oloc ; The buffer location. add bx,osiz ; Point past the data. mov [bx],al ; Add "$" for printing. mov ah,prstr mov dx,oloc int dos pop es ret PRTSCR 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 //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 640 msserv.asm /bin/echo -n ' '; /bin/ls -ld msserv.asm fi /bin/echo 'Extracting msset.asm' sed 's/^X//' <<'//go.sysin dd *' >msset.asm public setcom, status, stat0, baudprt, escprt, prmptr, dodef public setcpt, docom, shomac, atoi include msdefs.h setextra equ 100 macmax equ 20 ; max # of macros datas segment public 'datas' extrn comand:byte, flags:byte, trans:byte, cptfcb:byte, takadr:word extrn taklev:byte, inichk:byte, portval:word, curdsk:byte extrn setktab:byte, setkhlp:byte kerm db 'Kermit-MS>$' crlf db cr,lf,'$' crlfsp db cr,lf,' ' ; crlf space db '$' eqs db ' = $' ermes1 db cr,lf,'?Too many macros$' ermes2 db cr,lf,'?No room in table for macro$' ermes3 db cr,lf,'?Not confirmed$' ermes4 db cr,lf,'?No room in take stack to expand macro$' ermes5 db cr,lf,'?Not implemented$' erms23 db cr,lf,'?0 or null scan code not allowed$' ;[jd] erms24 db cr,lf,'?Capture file already open (use close command)$' ;[jd] filhlp db ' Input file specification for session logging$' macmsg db ' Specify macro name followed by body of macro $' shmmsg db ' Confirm with carriage return $' prmmsg db ' Enter new prompt string $' sk1msg db ' Decimal scan code for key $' sk2msg db ' Redefinition string for key $' prterr db '?Unrecognized value$' unrec db 'Baud rate is unknown$' defpmp db 'Definition string: $' esctl db 'Control-$' ; [6] nonmsg db 'none$' delmsg db 'delete$' onmsg db 'On' offmsg db 'Off' tmp db ?,'$' sum db 0 min db 0 max db 0 desta dw 0 numerr dw 0 numhlp dw 0 stflg db 0 ; Says if setting SEND or RECEIVE parameter. srtmp db 0 savsp dw 0 temp dw 0 temp1 dw ? ; Temporary storage. temp2 dw ? ; Temporary storage. locst db 'Local echo $' belon db 'Ring bell after transfer$' beloff db 'No bell after transfer$' vtemst db 'HEATH-19 emulation $' cm1st db 'Communications port: 1$' cm2st db 'Communications port: 2$' capmsg db 'Session logging $' eofmsg db 'EOF mode: $' flost db 'No flow control used$' floxmsg db 'Flow control: XON/XOFF $' handst db 'Handshake used: $' destst db 'File destination: $' diskst db 'Default disk: $' blokst db 'Block check used: $' ebyst db '8-bit quoting done only on request$' ebvst db '8-bit quoting will be done with: $' sqcst db 'Send cntrl char prefix: $' rqcst db 'Receive cntrl char prefix: $' debon db 'Debug mode $' flwon db 'Warning $' parmsg db 'Parity $' abfdst db 'Discard incomplete file$' abfkst db 'Keep incomplete file$' eolst db 'End-of-line character: $' ssohst db 'Send start-of-packet char: $' rsohst db 'Receive start-of-packet char: $' stimst db 'Send timeout (seconds): $' rtimst db 'Receive timeout (seconds): $' spakst db 'Send packet size: $' rpakst db 'Receive packet size: $' snpdst db '# of send pad chars: $' rnpdst db '# of receive pad chars: $' timmsg db 'Timer $' escmes db 'Escape character: $' b03st db 'Baud rate is 300$' b12st db 'Baud rate is 1200$' b18st db 'Baud rate is 1800$' b24st db 'Baud rate is 2400$' b48st db 'Baud rate is 4800$' b96st db 'Baud rate is 9600$' b04st db 'Baud rate is 45.5$' b05st db 'Baud rate is 50$' b07st db 'Baud rate is 75$' b11st db 'Baud rate is 110$' b13st db 'Baud rate is 134.5$' b15st db 'Baud rate is 150$' b06st db 'Baud rate is 600$' b20st db 'Baud rate is 2000$' b19st db 'Baud rate is 19200$' b38st db 'Baud rate is 38400$' eolhlp db cr,lf,'Decimal number between 0 and 31$' eolerr db cr,lf,'Illegal end-of-line character$' timerr db cr,lf,'Illegal timeout value$' timhlp db cr,lf,'Decimal number between 0 and 94$' soherr db cr,lf,'Illegal start-of-packet character$' quohlp db cr,lf,'Decimal number between 33 and 126$' quoerr db cr,lf,'Illegal control character prefix$' pakerr db cr,lf,'Illegal packet length$' pakhlp db cr,lf,'Decimal number between 20 and 94$' npderr db cr,lf,'Illegal number of pad characters$' npdhlp db cr,lf,'Decimal number between 0 and 99$' paderr db cr,lf,'Illegal pad character$' padhlp db cr,lf,'Decimal number between 0 and 31 or 127$' eschlp db cr,lf,'Enter literal value (ex: Cntrl ]) $' desterr db cr,lf,'Illegal destination device$' dskhlp db cr,lf,'Default disk drive to use, such as A:$' dskerr db cr,lf,'Invalid drive specification$' sethlp db cr,lf,'BAUD rate' db cr,lf,'BELL' db cr,lf,'BLOCK-CHECK-TYPE' db cr,lf,'DEBUG' db cr,lf,'DEFAULT-DISK' db cr,lf,'DESTINATION' db cr,lf,'END-OF-LINE character' db cr,lf,'EOF CTRL-Z or NOCTRL-Z' db cr,lf,'ESCAPE character change' db cr,lf,'FLOW-CONTROL' db cr,lf,'HANDSHAKE' db cr,lf,'HEATH-19' db cr,lf,'INCOMPLETE file' db cr,lf,'KEY' db cr,lf,'LOCAL-ECHO echoing (half-duplex)' db cr,lf,'PARITY type' db cr,lf,'PORT for communication' db cr,lf,'PROMPT' db cr,lf,'RECEIVE parameter' db cr,lf,'REMOTE on/off' db cr,lf,'SEND parameter' db cr,lf,'TAKE-ECHO' db cr,lf,'TIMER' db cr,lf,'WARNING' db '$' settab db 24 mkeyw 'BAUD',baudst mkeyw 'BELL',bellst mkeyw 'BLOCK-CHECK-TYPE',blkset mkeyw 'DEBUG',debst mkeyw 'DEFAULT-DISK',dskset mkeyw 'DESTINATION',desset mkeyw 'END-OF-LINE',eolset mkeyw 'EOF',seteof mkeyw 'ESCAPE',escape mkeyw 'FLOW-CONTROL',floset mkeyw 'HANDSHAKE',hndset mkeyw 'HEATH19-EMULATION',vt52em mkeyw 'INCOMPLETE',abfset mkeyw 'KEY',setkey mkeyw 'LOCAL-ECHO',lcal mkeyw 'PARITY',setpar mkeyw 'PORT',comset mkeyw 'PROMPT',promset mkeyw 'RECEIVE',recset mkeyw 'REMOTE',remset mkeyw 'SEND',sendset mkeyw 'TAKE-ECHO',takset mkeyw 'TIMER',timset mkeyw 'WARNING',filwar seoftab db 2 mkeyw 'CTRL-Z',1 mkeyw 'NOCTRL-Z',0 stsrtb db 06 ; Number of options. mkeyw 'PACKET-LENGTH',srpack mkeyw 'PADCHAR',srpad mkeyw 'PADDING',srnpd mkeyw 'QUOTE',srquo mkeyw 'START-OF-PACKET',srsoh mkeyw 'TIMEOUT',srtim ontab db 02H ; Two entries. mkeyw 'OFF',00H mkeyw 'ON',01H destab db 02H ; Two choices. mkeyw 'DISK',01H mkeyw 'PRINTER',00H ; What type of block check to use. blktab db 03H mkeyw '1-CHARACTER-CHECKSUM',1 mkeyw '2-CHARACTER-CHECKSUM',2 mkeyw '3-CHARACTER-CRC-CCITT',3 ; If abort when receiving files, can keep what we have or discard. [20d] abftab db 02H ; Only two options. mkeyw 'DISCARD',01H mkeyw 'KEEP',00H partab db 05H ; Five entries. [10 start] mkeyw 'EVEN',PAREVN mkeyw 'MARK',PARMRK mkeyw 'NONE',PARNON mkeyw 'ODD',PARODD mkeyw 'SPACE',PARSPC flotab db 2 mkeyw 'NONE',flonon mkeyw 'XON/XOFF',floxon hndtab db 7 mkeyw 'BELL',bell mkeyw 'CR',cr mkeyw 'ESC',esc mkeyw 'LF',lf mkeyw 'NONE',0 mkeyw 'XOFF',xoff mkeyw 'XON',xon BStab db 02H ;Two entries [19c start] mkeyw 'BACKSPACE',00H mkeyw 'DELETE',01H bdtab db 010H ; 16 entries mkeyw '110',b0110 mkeyw '1200',b1200 mkeyw '134.5',b01345 mkeyw '150',b0150 mkeyw '1800',b1800 mkeyw '19200',b19200 mkeyw '2000',b2000 mkeyw '2400',b2400 mkeyw '300',b0300 mkeyw '38400',b38400 mkeyw '45.5',b00455 mkeyw '4800',b4800 mkeyw '50',b0050 mkeyw '600',b0600 mkeyw '75',b0075 mkeyw '9600',b9600 ten dw 10 ; multiplier for setatoi rdbuf db 80H DUP(?) prm db 30 dup(0) ; Buffer for new prompt. prmptr dw kerm ; pointer to prompt defkw db 100 dup (?) macnum dw 0 ; one macro yet mactab dw ibmmac ; default ibm mac is macro 0 dw macmax dup (?) ; empty macro table defptr dw macbuf macbuf db macmax*100 dup (?) ; buffer for macro defs rmlft db setextra ; space left in set table mcctab db 1 ; macro cmd table, one initially mkeyw 'IBM',0 ; macro # 0 db setextra dup (?) ; room for more. ibmmac db imlen-1 db 'set timer on',cr,'set parity mark',cr db 'set local-echo on',cr,'set handshake xon',cr db 'set flow none',cr imlen equ $-ibmmac ; structure for status information stent struc sttyp dw ? ; type (actually routine to call) msg dw ? ; message to print val2 dw ? ; needed value: another message, or tbl addr tstcel dw ? ; address of cell to test, in data segment basval dw 0 ; base value, if non-zero stent ends sttab stent <onoff,vtemst,,flags.vtflg> stent <onoff,locst,,ecoflg,portval> stent <baudprt> stent <srchkw,parmsg,partab,parflg,portval> stent <onechr,escmes,,trans.escchr> stent <onoff,capmsg,,flags.capflg> stent <msg2,flost,floxmsg,floflg,portval> stent <prhnd> stent <srchkw,destst,destab,flags.destflg> stent <drnum,diskst,,curdsk> stent <onoff,flwon,,flags.flwflg> stent <msg2,beloff,belon,flags.belflg> stent <msg2,abfkst,abfdst,flags.abfflg> stent <srchkw,eofmsg,seoftab,flags.eofcz> stent <onechr,sqcst,,trans.rquote> stent <onechr,rqcst,,trans.squote> stent <onechr,rsohst,,trans.rsoh> stent <onechr,ssohst,,trans.ssoh> stent <stnum,rtimst,,trans.rtime> stent <stnum,stimst,,trans.stime> stent <stnum,rpakst,,trans.rpsiz> stent <stnum,spakst,,trans.spsiz> stent <stnum,snpdst,,trans.spad> stent <stnum,rnpdst,,trans.rpad> stent <onoff,timmsg,,flags.timflg> stent <pr8bit> stent <onechr,eolst,,trans.seol> stent <srchkw,blokst,blktab,trans.chklen> stent <msg2,cm2st,cm1st,flags.comflg> stent <onoff,debon,,flags.debug> dw 0 ; end of table sttbuf db 2000 dup (?) ; big buffer for status msg. datas ends code segment public extrn cmcfrm:near, prserr:near, comnd:near, dobaud:near extrn cmgtch:near, repars:near, coms:near, vts:near, defkey:near extrn inicpt:near, prompt:near, nout:near, prtscr:near extrn prkey:near assume cs:code,ds:datas ; This is the SET command. SETCOM PROC NEAR mov dx,offset settab ; Parse a keyword from the set table. mov bx,offset sethlp mov ah,cmkey call comnd jmp r call bx nop nop nop jmp rskp SETCOM endp docom proc near mov dx,offset mcctab mov bx,0 mov ah,cmkey call comnd jmp r push bx mov ah,cmcfm call comnd pop bx ret nop pop bx cmp taklev,maxtak ; room in take level? jl docom2 ; yes, continue mov dx,offset ermes4 ; else complain jmp reterr docom2: inc taklev ; increment take level (overflow) add takadr,size takinfo shl bx,1 mov si,mactab[bx] ; point to macro mov cl,[si] ; get size from macro mov ch,0 inc si ; point to actual definition mov bx,takadr ; point to current buffer mov [bx].takfcb,0ffh ; flag as a macro mov [bx].takptr,si ; point to beginning of def mov [bx].takchl,cl ; # of chars left in buffer mov [bx].takcnt,cx ; and in definition mov word ptr [bx].takcnt+2,0 ; zero high order... jmp rskp docom endp ; the define command dodef proc near cmp macnum,macmax ; get current macro count jl dode1 ; no, go on mov dx,offset ermes1 ; else complain jmp reterr ; and return dode1: mov ah,cmtxt mov bx,offset defkw+1 ; buffer for keyword mov dx,offset macmsg call comnd ret nop nop cmp ah,0 jne dode2 ret dode2: push es mov bx,ds mov es,bx cld mov cl,ah mov ch,0 ; length mov si,offset defkw+1 ; pointer to keyword mov ah,0 ; # of chars in keyword ; uppercase keyword, look for end dode3: lodsb ; get a byte cmp al,'a' jb dode4 cmp al,'z' ja dode4 sub al,'a'-'A' mov [si-1],al ; uppercase if necessary dode4: inc ah ; increment word count cmp al,' ' ; is it the break character? loopne dode3 ; no, loop thru rest of word dode5: jne dode6 ; ended with break char? dec ah ; yes, don't count in length dode6: mov defkw,ah ; store length in front of it add ah,4 ; add keyword overhead length cmp ah,rmlft ; will it fit in buffer jb dode7 ; yes, keep going mov dx,offset ermes2 ; else complain jmp reterr dode7: sub rmlft,ah ; subtract space used in tbl mov di,defptr ; pointer to free space inc macnum ; count the macro mov bx,macnum shl bx,1 ; double for word idx!!! mov mactab[bx],di ; install into table mov [di],cl ; store length inc di jcxz dode10 ; no copy if 0 length ; copy definition into buffer, changing commas to crs dode8: lodsb ; get a byte cmp al,',' ; comma? jne dode9 ; no, keep going mov al,cr ; else replace with cr dode9: stosb loop dode8 ; keep copying dode10: mov defptr,di ; update free ptr mov bl,defkw mov bh,0 lea di,defkw+1[bx] ; end of keyword mov al,'$' stosb mov ax,macnum stosb ; low-order mov al,0 ; high-order is always 0. stosb ; now install into table pop es mov bx,offset mcctab mov dx,offset defkw call addtab jmp rskp dodef endp ; add an entry to a keyword table ; enter with bx/ table address, dx/ ptr to new entry ; no check is made to see if the entry fits in the table. addtab PROC NEAR push es cld mov ax,ds mov es,ax ; address data segment mov bp,bx ; remember where tbl starts mov cl,[bx] ; pick up length of table mov ch,0 inc bx ; point to actual table... addta1: push cx ; preserve count mov si,dx ; point to entry lodsb ; get length of new entry mov cl,[bx] ; and length of table entry... mov ah,0 ; assume they're the same size cmp al,cl ; are they the same? lahf ; remember result of comparison... jae addta2 ; is new smaller? no, use table length mov cl,al ; else use length of new entry addta2: mov ch,0 lea di,[bx+1] ; point to actual keyword repe cmpsb ; compare strings pop cx ; restore count jb addta4 ; below, insert before this one jne addta3 ; not below or same, keep going sahf ; same. get back result of length comparison jb addta4 ; if new len is smaller, insert here jne addta3 ; if not same size, keep going mov si,bx ; else this is where entry goes jmp short addta6 ; no insertion required... addta3: mov al,[bx] mov ah,0 add bx,ax ; skip this entry add bx,4 ; len + $ + value... loop addta1 ; and keep looking addta4: mov si,bx ; this is first location to move mov di,bx inc ds:byte ptr [bp] ; remember we're adding one... jcxz addta6 ; no more entries, forget this stuff mov bh,0 ; this stays 0 addta5: mov bl,[di] ; get length lea di,[bx+di+4] ; end is origin + length + 4 for len, $, value loop addta5 ; loop thru remaining keywords mov cx,di sub cx,si ; compute # of bytes to move push si ; preserve loc for new entry mov si,di ; first to move is last dec si ; minus one mov di,dx ; new entry mov bl,[di] ; get length lea di,[bx+si+4] ; dest is source + length of new + 4 std ; move backwards rep movsb ; move the table down cld ; put flag back pop si addta6: mov di,si ; this is where new entry goes mov si,dx ; this is where it comes from mov cl,[si] ; length mov ch,0 add cx,4 ; overhead bytes rep movsb ; stick it in pop es ret ; and return addtab endp ; Show defined macros. SHOMAC PROC NEAR mov ah,cmtxt mov bx,offset rdbuf mov dx,offset shmmsg call comnd jmp r cmp ah,0 ; Bare CR means show all macros. jne shom2 ; No, he wants specific macro expanded. mov si,offset mcctab ; Table of macro names. lodsb mov cl,al ; Number of macro entries. mov ch,0 shom0: jcxz shom1 ; Done if none left to display. lodsb ; Length of macro name. push ax ; Don't forget it. mov ah,prstr mov dx,offset crlfsp ; Go to new line. int dos mov dx,si ; Print macro name. int dos mov dx,offset eqs int dos pop ax mov ah,0 add si,ax ; Skip over name. inc si ; Get to macro number. mov bx,[si] ; Pick it up. call expmac ; Expand the macro. dec cx add si,2 ; Skip over macro number. jmp shom0 ; And do the rest. shom1: mov ah,prstr mov dx,offset crlf int dos jmp rskp shom2: mov ah,prstr mov dx,offset ermes3 int dos jmp rskp SHOMAC ENDP ; Expand the macro, called with BX/macro number. expmac: push si push cx mov si,offset mactab ; Table of address expansions. shl bx,1 ; Double and use as index into table. mov si,[si+bx] ; Get address of expansion in question. mov ax,si ; Address of string. inc ax ; Don't print length. mov cl,[si] ; Length of string. mov ch,0 call prkey ; Print it. pop cx pop si ret seteof proc near mov ah,cmkey mov bx,0 mov dx,offset seoftab call comnd jmp r push bx mov ah,cmcfm call comnd jmp seteo1 ; error return... nop pop bx mov flags.eofcz,bl ; set value jmp rskp ; and return seteo1: pop bx ret seteof endp ; This is the ESCAPE character SET subcommand. [6 start] ESCAPE PROC NEAR call cmgtch ; Get a char. cmp ah,0 jns es1 ; Terminator or no? and ah,7FH ; Turn off minus bit. cmp ah,'?' jne es0 mov dx,offset eschlp mov ah,prstr int dos mov dx,offset crlf int dos mov dx,comand.cmprmp int dos mov bx,comand.cmdptr mov al,'$' mov [bx],al mov dx,offset comand.cmdbuf int dos dec comand.cmcptr ; Ignore dollar sign. dec comand.cmccnt mov comand.cmaflg,0 jmp repars es0: mov ah,prstr mov dx,offset ermes3 int dos ret es1: mov temp,ax call cmcfrm jmp es0 nop ; Take up 3 bytes. mov ax,temp mov trans.escchr,ah ; Save new value. ret ESCAPE ENDP ; [6 end] ; This is the End-of-line character SET subcommand. EOLSET PROC NEAR mov min,0 mov max,1FH mov sum,0 mov tmp,10 mov temp1,0 mov desta,offset trans.seol mov numhlp,offset eolhlp mov numerr,offset eolerr jmp num0 ; Common routine for parsing numerical input. EOLSET ENDP num0: call cmgtch ; Get the first char into AH. cmp ah,0 js num1 cmp ah,'0' jl num1 cmp ah,'9' ja num1 mov temp1,1 sub ah,30H mov dl,ah mov al,sum mul tmp add al,dl mov sum,al jmp num0 num1: and ah,7FH cmp ah,CR jne num2 cmp temp1,0 je num21 mov al,sum cmp al,min jl num3 cmp al,max jg num3 mov bx,desta mov [bx],al ret num2: cmp ah,03FH ; Question mark? je num4 num21: mov ah,prstr mov dx,offset ermes3 int dos jmp prserr num3: mov ah,prstr mov dx,numerr int dos jmp prserr num4: mov ah,prstr mov dx,numhlp int dos mov dx,offset crlf int dos mov dx,comand.cmprmp int dos mov bx,comand.cmdptr mov al,'$' mov [bx],al mov dx,offset comand.cmdbuf int dos dec comand.cmcptr ; Don't count the dollar sign. dec comand.cmccnt ; Or the question mark. mov comand.cmaflg,0 ; Check for more input. jmp repars ; This is the LOCAL echo SET subcommand. LCAL PROC NEAR mov dx,offset ontab mov bx,0 mov ah,cmkey call comnd jmp r push bx ; Save the parsed value. mov ah,cmcfm call comnd ; Get a confirm. jmp lcl0 ; Didn't get a confirm. nop pop bx mov si,portval mov [si].ecoflg,bl ; Set the local echo flag. mov [si].hndflg,bl ; This goes on/off with local echo. xor bl,01 ; Toggle this. mov [si].floflg,bl ; This is the opposite. ret lcl0: pop bx ret LCAL ENDP ; This is the VT52 emulation SET subcommand. VT52EM PROC NEAR call vts ret VT52EM ENDP ; This is the SET subcommand to choose between COM1 and COM2. [19b] COMSET PROC NEAR call coms ret COMSET ENDP FILWAR PROC NEAR mov dx,offset ontab mov bx,0 mov ah,cmkey call comnd jmp r push bx mov ah,cmcfm call comnd ; Get a confirm. jmp fil0 ; Didn't get a confirm. nop pop bx mov flags.flwflg,bl ; Set the filewarning flag. ret fil0: pop bx ret FILWAR ENDP ; This is the SET aborted-file command. [20d] ABFSET PROC NEAR mov dx,offset abftab mov bx,0 mov ah,cmkey call comnd jmp r push bx mov ah,cmcfm call comnd ; Get a confirm. jmp abf0 ; Didn't get a confirm. nop pop bx mov flags.abfflg,bl ; Set the aborted file flag. ret abf0: pop bx ret ABFSET ENDP ; This is the SET Parity command. [10 start] SETPAR PROC NEAR mov dx,offset partab mov bx,0 mov ah,cmkey call comnd jmp r push bx mov ah,cmcfm call comnd ; Get a confirm. jmp par0 ; Didn't get a confirm. nop pop bx mov si,portval mov [si].parflg,bl ; Set the parity flag. cmp bl,parnon ; Resetting parity to none? [21b] je setp0 ; Yes, reset 8 bit quote character. [21b] mov trans.ebquot,dqbin ; Else, do quoting. [21b] ret ; That's it. [21b] setp0: mov trans.ebquot,'Y' ; If none, say will quote upon request. [21b] ret par0: pop bx ret SETPAR ENDP ; [10 end] ; Sets debugging mode on and off. DEBST PROC NEAR mov dx,offset ontab mov bx,0 mov ah,cmkey call comnd jmp r push bx mov ah,cmcfm call comnd ; Get a confirm. jmp deb0 ; Didn't get a confirm. nop pop bx mov flags.debug,bl ; Set the DEBUG flag. ret deb0: pop bx ret DEBST ENDP ; Turn bell on or off. [17a start] BELLST PROC NEAR mov dx,offset ontab mov bx,0 mov ah,cmkey call comnd jmp r push bx mov ah,cmcfm call comnd jmp bel0 nop pop bx mov flags.belflg,bl ret bel0: pop bx ret BELLST ENDP ; [17a end] ; Toggle echo'ing of TAKE file to be either ON or OFF. TAKSET PROC NEAR mov dx,offset ontab mov bx,0 mov ah,cmkey call comnd jmp r push bx mov ah,cmcfm call comnd jmp tak0 nop pop bx mov flags.takflg,bl ret tak0: pop bx ret TAKSET ENDP ; [17a end] ; Set timer ON/OFF during file transfer. TIMSET PROC NEAR mov dx,offset ontab mov bx,0 mov ah,cmkey call comnd jmp r push bx mov ah,cmcfm call comnd jmp tim0 nop pop bx mov flags.timflg,bl ret tim0: pop bx ret TIMSET ENDP ; [17a end] ; Allow user to change the "Kermit-MS>" prompt. PROMSET PROC NEAR mov ah,cmtxt mov bx,offset prm ; Read in the prompt. mov dx,offset prmmsg call comnd jmp r cmp ah,0 ; Just a bare CR? jne prom0 mov ax,offset kerm jmp prom1 prom0: mov byte ptr [bx],'$' ; End of string. mov ax,offset prm prom1: mov prmptr,ax ; Remember it. jmp rskp PROMSET ENDP ; Set Flow-Control subcommand. FLOSET PROC NEAR mov dx,offset flotab xor bx,bx mov ah,cmkey call comnd jmp r push bx mov ah,cmcfm call comnd ; Get a confirm. jmp flox ; Didn't get a confirm. nop pop bx mov si,portval mov [si].flowc,bx ; Flow control value. cmp bx,0 ; Turning it off? je flo0 ; Yes. mov [si].floflg,1 ; Say we're doing flow control. mov [si].hndflg,0 ; So don't do handshaking. ret flo0: mov [si].floflg,bl ; Say we're not doing flow control. ret flox: pop bx ret FLOSET ENDP ; Set Handshake subcommand. HNDSET PROC NEAR mov dx,offset hndtab mov bx,0 mov ah,cmkey call comnd jmp r push bx mov ah,cmcfm call comnd ; Get a confirm. jmp hndx ; Didn't get a confirm. nop pop bx mov si,portval cmp bl,0 ; Setting handshake off? je hnd0 ; Yes. mov [si].floflg,0 ; Else, turn flow control off. mov [si].hndflg,1 ; And turn on handshaking. mov [si].hands,bl ; Use this char as the handshake. ret hnd0: mov [si].hndflg,0 ; No handshaking. mov [si].floflg,1 ; If one is off, the other is on. ret hndx: pop bx ret HNDSET ENDP ; Set block check type sub-command. BLKSET PROC NEAR mov dx,offset blktab mov bx,0 mov ah,cmkey call comnd jmp r push bx mov ah,cmcfm call comnd ; Get a confirm. jmp blk0 ; Didn't get a confirm. nop pop bx mov trans.chklen,bl ; Use this char as the handshake. mov inichk,bl ; Save here too. ret blk0: pop bx ret BLKSET ENDP ; Set destination for incoming file. DESSET PROC NEAR mov dx,offset destab mov bx,0 mov ah,cmkey call comnd jmp r push bx mov ah,cmcfm call comnd ; Get a confirm. jmp des0 ; Didn't get a confirm. nop pop bx mov flags.destflg,bl ; Set the destination flag. ret des0: pop bx ret DESSET ENDP ; Set default disk for sending/receiving, etc. DSKSET PROC NEAR mov comand.cmcr,1 ; Don't want filename specified. mov ah,cmifi ; Parse for drive specification. mov dx,offset rdbuf ; Read into handy buffer. mov bx,offset dskhlp ; Text of help message. call comnd jmp r mov ah,cmcfm call comnd jmp r cmp flags.nmoflg,0 ; Fail if specified file name. je dsk1 dsk0: mov ah,prstr mov dx,offset dskerr ; Illegal drive specification. int dos ret dsk1: mov bx,offset rdbuf mov ah,[bx] ; Get the drive they said to use. cmp ah,0 ; Did they type a bare CR? je dsk0 ; Yes, complain. mov curdsk,ah ; And remember it. dec ah mov dl,ah mov ah,seldsk int dos ret DSKSET ENDP ; This function sets the baud rate. BAUDST PROC NEAR mov dx,offset bdtab mov bx,0 mov ah,cmkey call comnd jmp r push bx mov ah,cmcfm call comnd ; Get a confirm. jmp bau0 ; Didn't get one. nop pop bx mov si,portval mov ax,[si].baud ; Remember original value. [25] mov [si].baud,bx ; Set the baud rate. call dobaud ; Use common code. [19a] ret bau0: pop bx ret BAUDST ENDP SENDSET PROC NEAR mov stflg,'S' ; Setting SEND parameter sndst0: mov dx,offset stsrtb ; Parse a keyword. mov bx,0 mov ah,cmkey call comnd jmp r call bx nop nop nop jmp rskp SENDSET ENDP recset: mov stflg,'R' ; Setting RECEIVE paramter. jmp sndst0 remset proc near mov ah,cmkey mov dx,offset ontab mov bx,0 call comnd jmp r push bx ; save parsed value mov ah,cmcfm call comnd ; confirm pop bx ret ; return on failure nop pop bx mov flags.remflg,bl ; set remote setting jmp rskp ; and return remset endp ; Set send/receive start-of-header. srsoh: mov min,0 mov max,1FH mov sum,0 mov tmp,10 mov desta,offset trans.ssoh ; Assume SEND. cmp stflg,'S' ; Setting SEND paramter? je srsoh0 mov desta,offset trans.rsoh srsoh0: mov numhlp,offset eolhlp ; Reuse help message. mov numerr,offset soherr mov temp1,0 jmp num0 ; Common routine for parsing numerical input. ; Set send/receive timeout. srtim: mov min,0 mov max,94 mov sum,0 mov tmp,10 mov desta,offset trans.stime ; Assume SEND. cmp stflg,'S' ; Setting SEND paramter? je srtim0 mov desta,offset trans.rtime srtim0: mov numhlp,offset timhlp ; Reuse help message. mov numerr,offset timerr mov temp1,0 jmp num0 ; Common routine for parsing numerical input. ; Set send/receive packet length. srpack: mov min,20 mov max,94 mov sum,0 mov tmp,10 mov desta,offset trans.spsiz cmp stflg,'S' ; Setting SEND paramter? je srpak0 mov desta,offset trans.rpsiz srpak0: mov numhlp,offset pakhlp mov numerr,offset pakerr mov temp1,0 jmp num0 ; Parse numerical input. ; Set send/receive number of padding characters. srnpd: mov min,0 mov max,99 mov sum,0 mov tmp,10 mov desta,offset trans.spad cmp stflg,'S' ; Setting SEND paramter? je srnpd0 mov desta,offset trans.rpad srnpd0: mov numhlp,offset npdhlp mov numerr,offset npderr mov temp1,0 jmp num0 ; Parse numerical input. ; Set send/receive padding character. srpad: mov min,0 mov max,127 mov sum,0 mov tmp,10 mov srtmp,0FFH ; Haven't seen anything yet. mov desta,offset srtmp mov numhlp,offset padhlp mov numerr,offset paderr mov temp1,0 mov savsp,sp call num0 ; Parse numerical input. mov sp,savsp mov temp,offset trans.spadch cmp stflg,'S' je srpad1 mov temp,offset trans.rpadch srpad1: mov bx,offset srtmp mov ah,[bx] cmp ah,0FFH ; Did they end up not doing the command? je srpad3 cmp ah,127 ; This is allowed. je srpad2 cmp ah,32 jb srpad2 ; Between 0 and 31 is OK too. mov ah,prstr mov dx,offset paderr int dos ret srpad2: mov bx,temp ; Set the real pad char. mov [bx],ah srpad3: ret ; Set send/receive control character prefix. srquo: mov min,33 mov max,126 mov sum,0 mov tmp,10 mov desta,offset trans.rquote ; Used for outgoing packets. cmp stflg,'S' ; Setting outgoing quote char? je srquo0 mov desta,offset trans.squote ; For incoming quote char. srquo0: mov numhlp,offset quohlp mov numerr,offset quoerr mov temp1,0 jmp num0 ; Parse numerical input. ; This is the STATUS command. STATUS PROC NEAR mov ah,cmcfm call comnd ; Get a confirm. jmp r ; Didn't get a confirm. mov dx,offset crlf mov ah,prstr int dos ; initial crlf call stat0 mov cx,di ; End of buffer sub cx,ax ; Get length of buffer. dec cx ; Account for null. mov di,ax ; Buffer pointer. call prtscr ; Put data onto the screen. jmp rskp STATUS ENDP ; Return a pointer to status message in AX, ptr to end in DI. STAT0 PROC NEAR push es mov ax,ds mov es,ax ; address data segment cld ; make sure strings go the right way mov di,offset sttbuf ; point to destination buffer mov bx,offset sttab ; table to control printing mov al,' ' ; start with a space stosb ; in the buffer mov ax,0 ; need-new-line flag stat01: cmp word ptr [bx],0 ; end of table? je stat02 ; yes, exit routine push bx push di ; remember important values push ax call [bx].sttyp ; call the appropriate routine pop ax pop cx ; return buffer value pop bx ; and ptr or ax,ax ; do we need a newline? jne stat03 ; yes, go put one in sub cx,di ; else see how many columns they used add cx,40 ; this is where we'd like to be ; if cx is negative here, we have a problem... mov al,' ' rep stosb ; add right # of spaces mov ax,1 ; note we need a newline next time jmp short stat04 ; and keep looping around stat03: mov cx,3 mov si,offset crlfsp rep movsb ; append crlf to string xor ax,ax ; reset newline flag stat04: add bx,size stent ; advance to next one jmp stat01 stat02: mov al,0 ; end buffer stosb mov ax,offset sttbuf pop es ; restore this ret ; and return STAT0 ENDP ; handler routines for status ; all are called with di/ destination buffer, bx/ stat ptr. They ; can change any register but the segment registers, must update ; di to the end of the buffer. ; copy the message into the buffer stmsg proc near mov si,[bx].msg ; get message address stms1: lodsb ; get a byte stosb ; drop it off cmp al,'$' ; end of message? jne stms1 ; no, keep going dec di ; else back up ptr ret ; and return stmsg endp ; get address of test value in stent. Returns address in si stval proc near mov si,[bx].basval ; get base value cmp si,0 ; any there? je stva1 ; no, keep going mov si,[si] ; yes, use as base address stva1: add si,[bx].tstcel ; add offset of test cell ret ; and return it stval endp ; print a single character onechr proc near call stmsg ; copy message part first call stval ; pick up test value address mov al,[si] ; this is char to print cmp al,' ' ; printable? jae onech1 ; yes, keep going add al,64 ; make printable. mov byte ptr [di],'^' inc di ; note ctrl char onech1: stosb ; drop char off ret ; and return onechr endp ; numeric field... stnum proc near call stmsg ; copy message call stval ; pick up value address mov al,[si] ; get value mov ah,0 ; high order is 0 call outnum ; put number into buffer ret ; and return stnum endp ; translate the number in ax... outnum proc near cwd mov bx,10 div bx ; divide to get digit push dx ; save remainder digit or ax,ax ; test quotient jz outnu1 ; zero, no more of number call outnum ; else call for rest of number outnu1: pop ax ; get digit back add al,'0' ; make printable stosb ; drop it off ret ; and return outnum endp ; on/off field onoff proc near call stmsg ; copy message call stval ; get value cell mov al,[si] mov si,offset onmsg mov cx,2 ; assume 2-byte 'ON' message or al,al ; test value jnz onof1 ; on, have right msg mov si,offset offmsg mov cx,3 onof1: rep movsb ; copy right message in ret ; and return onoff endp ; print first message if false, second if true msg2 proc near call stval ; get value cell mov al,[si] mov si,[bx].msg ; assume off or al,al ; is it? jz msg21 ; yes, continue mov si,[bx].val2 ; else use alternate message msg21: jmp stms1 ; handle copy and return msg2 endp ; search a keyword table for a value, print that value srchkw proc near call stmsg ; first print message call stval mov al,[si] ; get value to hunt for mov ah,0 ; high order is 0 mov bx,[bx].val2 ; this is table address jmp prttab ; and look in table. srchkw endp ; Print the drive name. drnum proc near call stmsg ; copy message part first call stval ; pick up test value address mov al,[si] ; this is char to print add al,'@' ; Make it printable. stosb mov byte ptr [di],':' inc di ; end with a colon ret ; and return drnum endp ; print 8-bit quoting pr8bit proc near mov bl,trans.ebquot ; get quote char mov si,offset ebyst ; assume no 8-bit quoting cmp bl,'Y' ; on request only? je pr8bi1 ; yes, continue mov si,offset ebvst ; else variable pr8bi1: call stms1 ; copy message in cmp bl,'Y' ; not doing it? je pr8bi2 ; no, forget this part mov [di],bl ; else drop off char too inc di pr8bi2: ret ; and return pr8bit endp ; Print the handshake. prhnd: mov si,offset handst ; copy in initial message call stms1 mov si,offset nonmsg ; assume no handshake mov bx,portval cmp [bx].hndflg,0 ; Is handshaking in effect? jne prh0 ; Yes, print what we're using. jmp stms1 ; no, say so and return prh0: mov al,'^' ; Doing handshaking with control char. stosb mov al,[bx].hands add al,40H ; Make printable. stosb ; put in buffer ret ; and return ; Print the pad character in AL. prpad: cmp al,127 ; Are they using a delete? jne prpad0 mov ah,prstr mov dx,offset delmsg int dos ret prpad0: mov dl,'^' mov ah,conout push ax int dos pop ax mov dl,al add dl,40H ; Make printable. int dos ret ; Print value from table. BX/address of table, AL/value of variable. prttab: mov cl,[bx] ; Number of entries in our table. inc bx ; Point to the data. prtt0: mov dl,[bx] ; Length of keyword. inc bx ; Point to keyword. mov dh,0 inc dx ; Account for "$" in table. mov si,dx ; Put to index register. cmp ax,[bx+si] ; Is this the one? je prtt1 add bx,dx ; Go to end of keyword. add bx,2 ; Point to next keyword. dec cl ; Any more keywords to check? jnz prtt0 ; Yes, go to it. mov bx,offset prterr prtt1: mov si,bx prtt2: jmp stms1 ; copy in message ret ; and return ; This routine prints out the escape character in readable format. ESCPRT PROC NEAR ; [6 start] mov dl,trans.escchr cmp dl,' ' jge escpr2 push dx mov ah,prstr mov dx,offset esctl int dos pop dx add dl,040H ; Make it printable. escpr2: mov ah,conout int dos ret ESCPRT ENDP ; [6 end] ; Print information on the baud rate. [19a] BAUDPRT PROC NEAR mov si,portval mov ax,[si].baud mov dx,offset b48st ; Assume 4800 baud. cmp ax,B4800 jnz bdprt0 jmp bdprt2 bdprt0: mov dx,offset b12st cmp ax,B1200 jnz bdprt1 jmp bdprt2 bdprt1: mov dx,offset b18st cmp ax,B1800 jz bdprt2 mov dx,offset b24st cmp ax,B2400 jz bdprt2 mov dx,offset b96st cmp ax,B9600 jz bdprt2 mov dx,offset b03st cmp ax,B0300 jz bdprt2 mov dx,offset b04st cmp ax,B00455 jz bdprt2 mov dx,offset b05st cmp ax,B0050 jz bdprt2 mov dx,offset b07st cmp ax,b0075 jz bdprt2 mov dx,offset b11st cmp ax,B0110 jz bdprt2 mov dx,offset b13st cmp ax,B01345 jz bdprt2 mov dx,offset b15st cmp ax,B0150 jz bdprt2 mov dx,offset b06st cmp ax,B0600 je bdprt2 mov dx,offset b20st cmp ax,B2000 jz bdprt2 mov dx,offset b19st cmp ax,B19200 jz bdprt2 mov dx,offset b38st cmp ax,B38400 jz bdprt2 mov dx,offset unrec ; Unrecognized baud rate. bdprt2: mov si,dx ; this is baud rate bdprt3: jmp stms1 ; go copy it and return BAUDPRT ENDP setkey proc near cmp setktab,0 ; any table? jne setk0 ; yes, use it mov dx,offset ermes5 jmp reterr ; else print error message setk0: mov dx,offset setktab ; set key options mov bx,offset setkhlp mov ah,cmkey call comnd jmp r cmp bx,-1 ;[jd] do we have scan code? jne setk1 ;[jd] yes, skip this part mov ah,cmtxt mov bx,offset rdbuf ; handy buffer mov dx,offset sk1msg call comnd jmp r ; fail return mov si,offset rdbuf ; this is parsed number call atoi ; Convert input to real number. jmp reterr ; No good. mov bx,ax ; put accumulation into bl setat3: cmp bx,0 ; is scan code 0? jne setk2 ; no, have scan code, look for def setk1: push bx ; save our scan code mov ah,cmcfm call comnd jmp short setkx ; no good, pop bx and return nop ; waste a byte pop bx ; scan code is in bl, ask for string part setk2: push bx mov dx,offset defpmp call prompt mov ah,cmtxt mov bx,offset rdbuf mov dx,offset sk2msg call comnd ; read the definition jmp short setkx ; pop bx and fail return nop mov cl,ah mov ch,0 ; set up length of definition pop ax ; get scan code back mov si,offset rdbuf ; point to definition call defkey ; go define the key ret ; use ret for now... jmp rskp ; and return setkx: pop bx ; pop junk off stack ret ; and return setkey endp ; Convert input in buffer pointed to by SI to real number which is returned ; in AX. Return on failure, return skip on success. ATOI PROC NEAR mov cl,ah ; Number of chars of input. mov ch,0 ; size of string jcxz atoi4 ; Fail on no input. mov ax,0 ; init sum mov bh,0 ; high order of this stays 0. atoi0: xchg al,bl ; save current sum lodsb ; grab a byte cmp al,' ' ; leading space? jne atoi1 ; no, continue xchg al,bl ; put sum back jmp short atoi2 ; and continue loop atoi1: cmp al,'9' ja atoi3 ; out of range, done cmp al,'0' jb atoi3 xchg al,bl ; put sum back into al mul ten ; shift one digit sub bl,'0' ; convert to binary add ax,bx ; add to sum atoi2: loop atoi0 ; loop thru all chars atoi3: jmp rskp atoi4: mov dx,offset erms23 ; complain and return ret ATOI ENDP ; addition for capture of raw output setcpt proc near test flags.capflg,0FFH jz setcp1 ; no capture file, keep going mov dx,offset erms24 jmp reterr setcp1: mov comand.cmcr,0 ; Filename must be specified. mov ah,cmifi mov dx,offset cptfcb mov bx,offset filhlp call comnd jmp r mov ah,cmcfm call comnd ; confirm with carriage return jmp r mov ah,delf mov dx,offset cptfcb int dos ; open up file mov ah,makef mov dx,offset cptfcb int dos mov cptfcb+32,0 call inicpt ; init capture variables mov flags.capflg,0FFH ; know we have capture routine jmp rskp ; and return setcpt 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 ; routine to print an error message, then retskp ; expects message in dx reterr proc near mov ah,prstr int dos jmp rskp reterr endp code ends end//go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 640 msset.asm /bin/echo -n ' '; /bin/ls -ld msset.asm fi
knutson@ut-ngp.UUCP (Jim Knutson) (10/05/84)
: Run this shell script with "sh" not "csh" PATH=:/bin:/usr/bin:/usr/ucb export PATH all=FALSE if [ $1x = -ax ]; then all=TRUE fi /bin/echo 'Extracting msterm.asm' sed 's/^X//' <<'//go.sysin dd *' >msterm.asm public clscpt, defkey, cptfcb, inicpt, clscpi, telnet public dopar, shokey, prkey include msdefs.h datas segment public 'datas' extrn flags:byte, trans:byte, buff:byte, portval:word targ termarg <0,1,80,24,cptchr,2dch,0,scntab,deftab,0,,parnon> ssp dw 0 ; Save SP in Telnet. crlf db cr,lf,'$' tmp db ?,'$' temp dw 0 temp1 dw ? ; Temporary storage. temp2 dw ? ; Temporary storage. tmsg1 db cr,lf,'[Connecting to host, type $' tmsg3 db ' C to return to PC]',cr,lf,cr,lf,cr,lf,'$' tmsg2 db cr,lf,'[Back at micro]',cr,lf,'$' erms22 db cr,lf,'?No capture file open$' ;[jd] esctl db 'Control-$' ; [6] inthlp db cr,lf,' ? This message' db cr,lf,' C Close the connection' db cr,lf,' S Status of the connection' db cr,lf,' B Send a break' db cr,lf,' M Toggle mode line' db cr,lf,' Q Quit logging' db cr,lf,' R Resume logging' db cr,lf,' 0 Send a null' db cr,lf,' Typing the escape character will send it to the host' db 0 intprm db 'Command>$' CPTFCB DB 25H DUP (?) CAPBUF DB 200 DUP (?) CAPBP DW ? CAPLFT DB ? SCNTLEN EQU 200 ; MAX # OF DEFINITIONS ONE can have defbsiz equ 400 ; combined length of all definitions... scntab dw scntlen dup (?) ; scan codes redefined deftab dw scntlen dup (?) ; pointer to definition strings defbuf db defbsiz dup (?) defptr dw defbuf ; pointer starts at beginning deflen dw defbsiz ; amt of space left in buffer sttmsg db 'Type space to continue$' shkmsg db cr,lf,'Press key: $' datas ends code segment public extrn comnd:near, outchr:near, stat0:near extrn escprt:near, clrbuf:near, term:near extrn cmblnk:near, locate:near, prtchr:near extrn beep:near, puthlp:near extrn serini:near,serrst:near, sendbr:near, showkey:near assume cs:code, ds:datas ; the show key command. shokey proc near mov ah,cmcfm ; confirm with carriage return call comnd jmp r ; uh oh... mov dx,offset shkmsg mov ah,prstr int dos ; print a prompt for it mov ax,offset targ ; give it terminal arg block. call showkey ; show them the key definition push ax push cx ; save results mov dx,offset crlf mov ah,prstr int dos pop cx pop ax call prkey ; print the buffer mov dx,offset crlf mov ah,prstr int dos jmp rskp ; and return shokey endp ; pass a string pointer in ax, length in cx. ; Prints the string, quoting any unprintables, except crlf. prkey proc near mov si,ax ; copy string ptr jcxz prke6 ; no string, stop here prke1: push cx ; save counter lodsb ; get a byte and al,7fH ; only consider low-order 7 bits. cmp al,' ' ; printable? jb prke2 ; no, print the hard way cmp al,7fH ; maybe a delete? jne prke4 ; no, can just put into string prke2: jcxz prke3 ; last char, can't be crlf cmp al,cr ; carriage return? jne prke3 ; no, go on cmp byte ptr [si],lf ; followed by linefeed? jne prke3 mov ah,prstr mov dx,offset crlf int dos ; else just print crlf inc si ; skip over lf pop cx ; careful... dec cx push cx jmp short prke5 prke3: push ax ; preserve the char mov ah,conout mov dl,'\' int dos ; print the quote character pop ax call proct ; print the octal byte jmp short prke5 prke4: mov dl,al ; normal char, just print it mov ah,conout int dos prke5: pop cx ; restore count loop prke1 prke6: ret ; and return prkey endp ; print the byte in al as an octal number proct proc near mov dl,al ; get the byte and dl,7h ; keep low-order byte mov cl,3 shr al,cl ; shift to get next digit jz proc1 ; 0, no more to print push dx ; else save current digit call proct ; print rest pop dx proc1: mov ah,conout add dl,'0' ; make printable int dos ret proct endp ; This is the CONNECT command. TELNET PROC NEAR mov ah,cmcfm call comnd ; Get a confirm. jmp r ; Didn't get a confirm. mov ah,prstr ; Output mov dx,offset crlf ; a crlf. int dos call domsg ; Reassure user. [19b] mov al,targ.flgs ; get present flags and al,modoff ; this is only one we can keep around or al,havtt ; defaults (!) cmp flags.debug,0 ; debug mode? jz tel0 ; no, keep going or al,trnctl ; yes, show control chars tel0: cmp flags.vtflg,0 ; vt52 emulation? jz tel1 or al,emheath tel1: mov bx,portval cmp [bx].ecoflg,0 ; echoing? jz tel2 or al,lclecho tel2: mov targ.flgs,al ; store flags mov ah,flags.comflg mov targ.prt,ah ; Port 1 or 2 mov ah,trans.escchr mov targ.escc,ah mov ah,[bx].parflg mov targ.parity,ah mov ax,[bx].baud mov targ.baudb,al mov ah,flags.capflg and ah,capt or targ.flgs,ah call serini ; init serial port tem: mov ax,offset targ ; Point to terminal arguments call term or targ.flgs,scrsam ; assume screen is the same. intchr: mov ah,dconio ; Direct console I/O. mov dl,0FFH ; Input. int dos ; Get a char. jz intchr ; no char, keep looking mov ah,al jz intchr ; If so, go until we get a char. cmp ah,' ' ; space - ignore it je tem mov bh,ah ; Save the actual char. and ah,not ('a'-'A') ; Convert to upper case. or ah,40H ; convert ctl-char to actual char. cmp ah,'C' ; Is it close? jne intch1 call serrst ; reset serial port jmp rskp ; and return intch1: cmp ah,'S' ; Is it status? jnz intch2 call stat0 ; If so, call stat0. call puthlp ; put help on screen mov dx,offset sttmsg mov ah,prstr int dos intch1a:mov ah,8 ; console input, no echo int dos cmp al,' ' ; space? jne intch1a and targ.flgs,not scrsam ; remember screen changed. jmp tem intch2: cmp ah,'B' ; Send a break? [20g] jne intch3 ; No. [20g] call sendbr ; Yes, so send a break. [20g] jmp tem ; And return. [20g] intch3: cmp ah,'M' ; mode line? jne intch4 xor targ.flgs,modoff ; toggle mode line jmp tem ; and reconnect intch4: cmp bh,'?' ; Is it help? jne intch5 ; If not, go to the next check. mov ax,offset inthlp ; If so, get the address of the help message. call puthlp ; write help msg mov dx,offset intprm mov ah,prstr ; Print it. int dos and targ.flgs,not scrsam ; remember screen changed jmp intchr ; Get another char. intch5: cmp bh,trans.escchr ; Is it the escape char? jne intch7 ; If not, go send a beep to the user. intch6: mov ah,al call outchr nop nop nop jmp tem ; Return, we are done here. intch7: cmp ah,'Q' ; maybe want to stop logging? jne intch8 test targ.flgs,capt ; not capturing, can't do this jz intc10 and targ.flgs,not capt ; stop capturing jmp tem ; and resume intch8: cmp ah,'R' ; maybe resume? jne intch9 ; no, keep going cmp flags.capflg,0 ; can we capture? jz intc10 ; no, forget it test targ.flgs,capt ; already capturing? jnz intc10 ; yes, can't toggle back on then or targ.flgs,capt ; else turn flag on jmp tem ; and resume intch9: cmp bh,'0' ; perhaps want a null (note original chr in bh) jne intc10 mov ah,0 call outchr nop nop nop jmp tem intc10: call beep jmp tem TELNET ENDP ; Reassure user about connection to the host. Tell him what escape ; sequence to use to return and the communications port and baud ; rate being used. [19b] DOMSG PROC NEAR mov ah,prstr mov dx,offset tmsg1 int dos call escprt mov ah,prstr mov dx,offset tmsg3 int dos ret DOMSG ENDP ; Set parity for character in Register AL. dopar: push bx mov bx,portval cmp [bx].parflg,parnon ; No parity? [10 start] je parret ; Just return cmp [bx].parflg,parevn ; Even parity? jne dopar0 and al,07FH ; Strip parity. jpe parret ; Already even, leave it. or al,080H ; Make it even parity. jmp parret dopar0: cmp [bx].parflg,parmrk ; Mark parity? jne dopar1 or al,080H ; Turn on the parity bit. jmp parret dopar1: cmp [bx].parflg,parodd ; Odd parity? jne dopar2 and al,07FH ; Strip parity. jpo parret ; Already odd, leave it. or al,080H ; Make it odd parity. jmp parret dopar2: and al,07FH ; Space parity - turn off parity bit. parret: pop bx ret ; [10 end] inicpt proc near mov capbp,offset capbuf mov caplft,128 ; init buffer ptr & chrs left ret ; and return inicpt endp cptchr proc near ; capture routine, char in al push di mov di,capbp mov byte ptr [di],al inc di mov capbp,di ; restore pointer pop di dec caplft ; decrement chars remaining jnz cptch1 ; more room, forget this part call cptdmp ; dump the info call inicpt ; re-init ptrs. cptch1: ret ; and return cptchr endp cptdmp proc near ; empty the capture buffer push ax push dx mov ah,setdma mov dx,offset capbuf ; the capture routine buffer int dos mov ah,writef mov dx,offset cptfcb int dos ; write out the block ;*** must be fixed... check error returns, disable capturing, ;*** figure out how to put dma address back mov dx,offset buff mov ah,setdma int dos ; put dma back pop dx pop ax ret cptdmp endp clscpt proc near test flags.capflg,0FFH ; doing capture jnz clscp1 ; yes, go ahead mov dx,offset erms22 mov ah,prstr int dos jmp rskp clscp1: mov ah,cmcfm call comnd jmp r clscpi: mov al,'Z'-64 ; control-z for eof... call cptchr ; output to file mov al,caplft cmp al,128 ; is buffer empty? je clscp2 ; yes, forget this stuff call cptdmp ; dump buffer (preserves registers) clscp2: mov ah,0 sub word ptr cptfcb+16,ax ; subtract remaining from low filsize sbb word ptr cptfcb+18,0 ; and from high size (with borrow) mov ah,closf mov dx,offset cptfcb int dos ; close up file mov flags.capflg,0 ; no longer capturing... jmp rskp ; and return clscpt endp ; enter with ax/scan code to define, si/ pointer to definition, cx/ length ; of definition. Defines it in definition table. ;*** somewhere should check for overflow etc of defbuf, and of scntab defkey proc near push ax ; save scan code mov ax,ds mov es,ax ; address data segment mov di,defptr ; this is where the def gets built inc di ; leave a byte for length defk1: lodsb ; get a byte from the source cmp al,'\' ; escape? jne defk2 ; no, just deposit him dec cx ; count available is one less call trnesc ; translate the escape sequence inc cx ; account for '\' (loop will decrement again). defk2: stosb ; drop off character loop defk1 ; and keep going while we have more mov ax,di ; get ptr to end dec ax ; back up pointer to end mov si,defptr ; pick up old ptr value sub ax,si ; this is actual length used mov byte ptr [si],al ; fill in length of entry mov defptr,di ; this is next free byte ; definition address is in si pop ax ; recover scan code mov cx,targ.klen ; length of scan table jcxz defk4 ; not there, just go add it mov di,offset scntab ; the scan code table repne scasw ; look for this one jne defk4 ; not defined already sub di,offset scntab + 2 ; compute index into table mov deftab[di],si ; fill in address ret ; and return defk4: mov di,targ.klen ; get length again inc di cmp di,scntlen ja defk5 ;** ignore def if over size mov targ.klen,di ; update length shl di,1 ; double for word index mov scntab[di-2],ax ; put scan code into table mov deftab[di-2],si ; and fill in definition defk5: ret ; that's it defkey endp ; enter with si/ source pointer, cx/ count ; converts an escape sequence, updates all pointers trnesc proc near push bx push dx ; preserve these mov al,0 ; this is current accumulation jcxz trnes2 ; empty string, forget it mov bl,3 ; this is max # of digits to use mov bh,8 ; this is radix trnes1: mov dl,[si] cmp dl,'0' jb trnes2 ; out of range, stop here cmp dl,'7' ja trnes2 inc si ; accept character sub dl,'0' ; convert to binary mul bh ; shift accumulation add al,dl ; add to accumulation dec bl ; decrement digit counter loopnz trnes1 ; and keep trying trnes2: pop dx pop bx ret ; and return trnesc 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 //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 640 msterm.asm /bin/echo -n ' '; /bin/ls -ld msterm.asm fi /bin/echo 'Extracting msapc.hlp' sed 's/^X//' <<'//go.sysin dd *' >msapc.hlp This note describes the capabilities of MS-DOS MSKermit as implemented for the NEC Advanced Personal Computer (APC). The APC system dependent portion of the code (both port and terminal handlers) resides in the file MSXAPC.ASM. This is the only module required in addition to those that all implementations need. MSKermit on the APC supports both the standard serial port (as port 1) and the optional (H14) add-on serial port (as port 2). Port selection is performed using the SET PORT command. Any baud rate up to 38400 is legal although 38400 has never been tested and may not work well. The port is always configured as 8 data bits, no parity, and 1 stop bit. Any necessary parity is supplied by Kermit. The interrupt vector used by the optional port is jumper-selectable. Kermit is set up to use IR8 by default, so if another vector is required the MSXAPC.ASM file must be altered and reassembled. The changes are well commented, and only amount to changing the value of a conditional. Terminal mode support on the APC is relatively primitive. The only provisions that have been made for emulation are in the operating system firmware, which supports a limited subset of both VT100/ANSI and ADM-3A commands. Two pages of screen memory and rollback are also provided by the firmware. Control-uparrow scrolls back one line, while control-downarrow scrolls forward a line. Screen printing is performed by the CRTDUMP resident extension to MS-DOS using the control-print command. The print (or control-P) key alone causes Kermit to toggle echoing of the screen display to the printer. Key redefinition is provided, but only for the keyboard keys. The function keys must be defined using the system's KEY program. There is no direct access to the keyboard's scan codes on the APC, so instead each key is redefined by its ASCII value, limiting the usefulness of this function. The default escape character is control-]. Bugs in the firmware prevent this control sequence from being returned to the program, however, so it is necessary to instead use the left arrow key which sends the control-] code. Another alternative is to redefine the escape character in your MSKERMIT.INI initialization file. Despite these limitations, MSKermit for the APC is being released so that the benefits of its file transfer capability are available to APC MS-DOS users. It is to be hoped that these users will take it on themselves to enhance the capabilities. //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 640 msapc.hlp /bin/echo -n ' '; /bin/ls -ld msapc.hlp fi /bin/echo 'Extracting mskermit.hlp' sed 's/^X//' <<'//go.sysin dd *' >mskermit.hlp KERMIT VERSION 2.26 FOR MS-DOS AND PC-DOS July 26, 1984 Kermit-MS is a program that provides terminal emulation and file trans- fer for Intel 8088- and 8086-based microcomputers running the MS-DOS or PC-DOS operating system. The Kermit file transfer protocol was developed and the Kermit-MS program were developed at the Columbia University Center for Computing Activities. * Program Operation Kermit-MS can be run interactively, from a batch file, or as an "external" DOS command. Commands consist of one or more fields, separated by "whitespace" -- one or more spaces or tabs. Upon startup, the program executes any commands found in the file MSKERMIT.INI in the current path. X. Interactive Operation: To run Kermit-MS interactively, invoke the program from DOS command level by typing its name. When you see the command's prompt, "Kermit-MS>", you may type Kermit commands repeatedly until you are ready to exit the program. You can use these special characters while typing commands. BACKSPACE Delete the character most recently typed. May be typed repeatedly to delete backwards. You may also use DELETE, RUBOUT, or equivalent keys. CTRL-W Delete the most recent "word", or field, on the command line. May be typed repeatedly. CTRL-U Delete the entire command line. CTRL-C Cancel the current command and return to the "Kermit-MS>" prompt. ? Type a brief message describing what you are expected to type in the current field. ESC If enough characters have been supplied in the current field (keyword or file name) to uniquely identify it, supply the remainder of the field and position to the next field of the command. Otherwise, sound a beep. = Wildcard character for matching single characters in filenames, equivalent to MS-DOS "?". X. Command Line Invocation: Kermit-MS may also be invoked with command line arguments from DOS com- mand level, for instance: A>kermit send foo.bar or A>kermit set port 1, set baud 9600, connect When invoked with command line arguments, Kermit-MS will behave as if it were an external DOS command, like MODE. Note that several commands may be given on the command line, separated by commas. * Kermit-MS Commands Kermit-MS V2.26 has the following commands: BYE to remote server. CLOSE log file and stop logging remote session. CONNECT as terminal to remote system. DEFINE macros of Kermit-MS commands. DELETE local files. DIRECTORY listing of local files. DO a macro expansion. EXIT from Kermit-MS. FINISH Shut down remote server. GET remote files from server. HELP about Kermit-MS. LOCAL prefix for local file management commands. LOG remote terminal session. LOGOUT remote server. PUSH to MS-DOS command level. QUIT from Kermit-MS RECEIVE files from remote Kermit. REMOTE prefix for remote file management commands. RUN an MS-DOS program. SEND files to remote Kermit. SERVER mode of remote operation. SET various parameters. SHOW various parameters. SPACE inquiry. STATUS inquiry. TAKE commands from file. The following SET commands are available in Kermit-MS: BAUD Communications port line speed BELL Whether to beep at the end of a transaction BLOCK-CHECK-TYPE Level of error checking for file transfer DEBUG Display packet contents during file transfer DEFAULT-DISK Default disk drive for file i/o DESTINATION Default destination device for incoming files END-OF-LINE Packet terminator EOF Method for determining or marking end of file ESCAPE Escape character for CONNECT FLOW-CONTROL Enable or disable XON/XOFF HANDSHAKE Half-duplex line turnaround option HEATH19 Heath/Zenith-19 terminal emulation INCOMPLETE What to do with an incompletely received file KEY Specify key redefinitions, or "keystroke macros" LOCAL-ECHO Specify which host does the echoing during CONNECT PARITY Character parity to use PORT Select a communications port PROMPT Change the "Kermit-MS>" prompt to something else RECEIVE Request remote Kermit to use specified parameters REMOTE For running Kermit-MS interactively from back port SEND Use the specified parameters during file transfer TAKE-ECHO Control echoing of commands from TAKE files TIMER Enable/disable timeouts during file transfer WARNING Specify how to handle filename collisions The STATUS command shows the values of parameters which may be SET. * Command Macros Kermit-MS provides a facility for combining commands into "macros." Command macro definitions may be included in your MSKERMIT.INI file, TAKEn explicitly from a specified file, or typed interactively, and may be invoked with the DO command. * Command Macros Kermit-MS command macros are constructed with the DEFINE command. The syntax is DEFINE macro-name [command [, command [, ...]]] Any Kermit-MS commands may be included. Example: define telenet set parity mark, set baud 1200, connect A Kermit-MS command macro is invoked using the DO command. For in- stance, Kermit-MS comes with a predefined macro to allow convenient setup for IBM communications; to invoke it, you would type do ibm The IBM macro is defined as "parity mark, handshake xon, local-echo on, timer on". You can delete or replace this definition by adding a new (perhaps null) definition, such as define ibm parity even, handshake cr, local-echo on, timer on] or define ibm Command macro definitions can be displayed with the SHOW MACROS command. * Terminal Emulation Here are the terminal emulation options for the systems presently sup- ported by Kermit-MS: System EscChar Cabilities Terminal Service IBM PC, XT ^] R M P K Heath19 emulation DEC Rainbow ^] R P K VT102 firmware HP-150 ^] R HP-2623 firmware Wang PC ^A Wang firmware Generic DOS ^] Depends on system Under Capabilities, R means rollback, M means mode line, P means printer control, and K means key redefinition. IBM PC/XT Kermit can disable Heath-19 emulation and use an external con- sole device driver like ANSI.SYS instead. The escape character is used to regain the attention of Kermit-MS. When you type the escape character, Kermit-MS waits for you to follow it with a single character command: ? Help -- prints the available single-character commands. C Close the connection and return to Kermit-MS prompt level. S Show the status of the connection. B Send a BREAK signal to the port. 0 (the digit zero) Send a NUL (ASCII 0) to the port. Q Temporarily quit logging the remote session. R Resume logging the remote session. M Toggle the mode line, i.e. turn it off if it is on & vice versa. ^] (or whatever you have set the escape character to be) Typing the escape character twice sends one copy of it to the con- nected host. Typing any other character (except the space bar, which is the "null command") after the escape character will cause Kermit-MS to beep, but will do no harm. The escape character can be changed to something other than Control-Rightbracket by using the SET ESCAPE command. Kermit-MS includes several advanced features for use during terminal emulation, including screen scroll, printer control, and key redefini- tions. X. Screen Scroll Kermit-MS provides several pages of screen memory, which may be scrolled up and down using keys as follows: Function IBM PC/XT Rainbow HP-150 Screen Down PgDn PrevScreen Prev Line Down Ctrl-PgDn Ctrl-PrevScreen Shift-UpArrow Screen Up PgUp NextScreen Next Line Up Ctrl-PgUp Ctrl-NextScreen Shift-DownArrow Top of Memory Home Bottom of Memory End X. Printer Control A locally attached printer may be controlled in the normal manner, on most systems. Pushing the "Print Screen" key (shifted on some systems) will cause the current contents of the screen to be printed or spooled; holding down CTRL while depressing Print Screen will start or stop the spooling of incoming characters to the printer. CTRL-Print-Screen can be simulated with the Kermit-MS LOG PRN and CLOSE commands. X. Key Redefinitions Use SHOW KEY to find out the scan code of the key you want to redefine, then use SET KEY SCAN xxx to define the new value. Control characters are entered in the definition string as \ooo (a backslash followed by 2 or 3 octal digits denoting the ASCII value of the character). //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 640 mskermit.hlp /bin/echo -n ' '; /bin/ls -ld mskermit.hlp fi /bin/echo 'Extracting msmkboo.c' sed 's/^X//' <<'//go.sysin dd *' >msmkboo.c X/* MSNKBOO.C * * This program takes a file and encodes it into printable characters. * These printable files can then be decoded by the programs MSPCBOOT.BAS * or MSPCTRAN.BAS as the need may be. The file is encoded by taking * three consecutive eight bit bytes and dividing them into four six bit * bytes. An ASCII zero was then added to the resulting four characters. * to make them all printable ASCII characters in the range of the * character zero to the character underscore. In order to reduce the * size of the file null repeat count was used. The null repeat count * compresses up to 78 consecutive nulls into only two characters. This * is done by using the character tilde (~) as an indication that a group * of repetitive nulls has occured. The character following the tilde is * number of nulls in the group. The number is also converted in to a * printable character by adding an ASCII zero. The highest number of * nulls is therefore the highest printable character tilde. This is * equal to tilde minus zero nulls or 78 nulls. Because of the three * byte to four byte encoding the repeat counting can only start with * the first character of a three byte triplet. * * This C program was written specifically for the DEC-20 and as such * will not easily be transported to another system. The main problem * lies in the file I/O routines. It is necessary to make sure that * untranslated eight bit bytes are input from the input file. The * main change would be to make the OPEN statement reflect this for * your particular system and brand of UNIX and C. The rest of the * program should be transportable with little or no problems. * */ #include <stdio.h> /* Standard UNIX i/o definitions */ #include <file.h> X/* Symbol Definitions */ #define MAXPACK 80 /* Maximum packet size */ #define MYRPTQ '~' /* Repeat count prefix I will use */ #define DATALEN 78 /* Length of data buffer */ #define TRUE -1 /* Boolean constants */ #define FALSE 0 X/* Macros */ #define tochar(ch) ((ch) + '0') X/* Global Variables */ int size, /* Size of present data */ maxsize, /* Max size for data field */ nc, /* Number of input chars */ oc, /* Number of output chars */ fd, /* File pointer of file to read/write */ ofd, rpt, /* repeat count */ rptq, /* repeat quote */ rptflg, /* repeat processing flag */ eoflag, /* Set when file is empty. */ otot; /* What char number we are processing. */ char t, /* Current character */ one, two, three, *filnam, /* Current file name */ *ofile, packet[MAXPACK]; /* Packet buffer */ main(argc,argv) /* Main program */ int argc; /* Command line argument count */ char **argv; /* Pointers to args */ { char sfile(); /* Send file routine & ret code */ if (--argc != 2) usage(); /* Make sure there's a command line. */ rptq = MYRPTQ; /* Repeat Quote */ rptflg = TRUE; /* Repeat Count Processing Flag */ filnam = *++argv; /* Get file to send */ ofile = *++argv; /* Output file to create */ sfile(); printf("Done, in: %d, out: %d, efficiency: %.2f%%\n",nc,oc,(100.0*nc)/oc); } X/* S F I L E - Send a whole file */ char sfile() /* Send a file */ { char *i; fd = open(filnam,FATT_RDONLY|FATT_BINARY|FATT_DEFSIZE); if (fd < 0) /* Report any errors */ { printf("\n?Error opening file \"%s\"\n",filnam); exit(1); } ofd = open(ofile,FATT_WRONLY|FATT_CREATE|FATT_BINARY); if (ofd < 0) { printf("\n?error opening file \"%s\"\n",ofile); exit(1); } oc = strlen(filnam); /* Get the string length. */ for (i=filnam; *i != '\0'; i++) /* Uppercase the file name. */ if (*i >= 'a' && *i <= 'z') *i ^= 040; write(ofd,filnam,oc); /* Write the file name in the file. */ write(ofd,"\r\n",2); maxsize = DATALEN - 5; rpt = 0; /* Zero the repeat count. */ oc = nc = 0; /* Output & input character counts. */ otot = 1; /* Start with first char of triplet. */ while (getbuf() > 0) /* While not EOF, get a packet. */ { while (size < DATALEN - 1) packet[size++] = ' '; packet[size++] = '\r'; /* Explicit CRLF. */ packet[size++] = '\n'; packet[size] = '\0'; oc += size; /* Count output size. */ write(ofd,packet,size); /* Write the packet to the file. */ printf("%d: %s",size,packet); /* Print on the screen for testing. */ } } X/* G E T B U F -- Do one packet. */ getbuf() /* Fill one packet buffer. */ { if (eoflag != 0) return(-1); /* If at the end of file, stop. */ size = 0; while((t = getch()) >= 0) /* t == -1 means EOF. */ { nc++; /* Count the character. */ process(t); /* Process the character. */ if (size >= maxsize) /* If the packet is full, */ { packet[size] = '\0'; /* terminate the string. */ return(size); } } eoflag = -1; /* Say we hit the end of the file. */ process(0); /* Clean out any remaining chars. */ process(0); process(' '); packet[size] = '\0'; /* Return any partial final buffer. */ return(size); } X/* P R O C E S S -- Do one character. */ process(a) char a; { if (otot == 1) /* Is this the first of three chars? */ { if (a == 0) /* Is it a null? */ { if (++rpt < 78) /* Below max nulls, just count. */ return; else if (rpt == 78) /* Reached max number, must output. */ { packet[size++] = rptq; /* Put in null repeat char and */ packet[size++] = tochar(rpt); /* number of nulls. */ packet[size] = '\0'; rpt = 0; return; } } else { if (rpt == 1) /* Just one null? */ { one = 0; /* Say the first char was a null. */ two = a; /* This char is the second one. */ otot = 3; /* Look for the third char. */ rpt = 0; /* Restart null count. */ return; } if (rpt > 1) /* Some number of nulls? */ { packet[size++] = rptq; /* Insert the repeat prefix */ packet[size++] = tochar(rpt); /* and count. */ packet[size] = '\0'; rpt = 0; /* Reset repeat counter. */ } one = a; /* Set first character. */ otot = 2; /* Say we are at the second char. */ } } else if (otot == 2) { two = a; /* Set second character. */ otot = 3; /* Say we are at the third char. */ } else { three = a; otot = 1; /* Start over at one. */ pack(one,two,three); /* Pack in the three characters. */ } } X/* This routine does the actual three character to four character encoding. * The concept is relatively straight forward. The first output character * consists of the first (high order or most significant) six bits of the * first input character. The second output character is made from the * remaining two low order bits of the first input character and the first * four high order bits of the second input character. The third output * character is built from the last four low order bits of the second input * character and the two high order bits of the third input character. The * fourth and last output character consists of the six low order bit of * the third input character. In this way the three eight bit input char- * acters (for a total of 24 bits) are divided into four six bit output * characters (also for a total of 24 bits). In order to make the four * output characters printable an ASCII zero is then added to each of them. * */ pack(x,y,z) char x,y,z; { packet[size++] = tochar((x >> 2) & 077); packet[size++] = tochar(((x & 003) << 4) | ((y >> 4) & 017)); packet[size++] = tochar(((y & 017) << 2) | ((z >> 6) & 003)); packet[size++] = tochar(z & 077); packet[size] = '\0'; } getch() /* Get next (or pushed) char. */ { char a; return((read(fd,&a,1) > 0) ? a : -1); /* (or -1 if EOF) */ } usage() /* Give message if user makes */ { /* a mistake in the command. */ fprintf(stderr,"usage: msmkboo inputfile outputfile\n"); exit(1); } //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 640 msmkboo.c /bin/echo -n ' '; /bin/ls -ld msmkboo.c fi /bin/echo 'Extracting mspctran.bas' sed 's/^X//' <<'//go.sysin dd *' >mspctran.bas 1 'Use this BASIC program on the PC if you have the printable file 2 'MSKERMIT.BOO already on the PC to convert it to an executable 3 'file. This program takes about 30 minutes to run on a PC with 4 'floppy disks. 5 ' Bill Catchings, June 1984 6 ' Columbia University Center for Computing Activities 10 t$ = time$ ' Save the time. 20 defint a-z ' Integer to gain some speed. 30 n$ = chr$(0) 40 z = asc("0") 50 t = asc("~")-z 60 def fnuchr%(a$)=asc(a$)-z 70 open "MSKERMIT.BOO" for input as #1 100 input#1,f$ ' Is this the right file? 110 if len(f$) > 20 then goto 900 120 open f$ for output as #2 130 print "Outputting to "+f$ 200 if eof(1) then goto 800 ' Exit nicely on end of file. 210 input#1,x$ ' Get a line. 220 y$ = "" ' Clear the output buffer. 230 goto 400 300 print#2,y$; ' Print output buffer to file. 310 goto 200 ' Get another line. 400 if len(x$) < 4 goto 300 ' Is the input buffer empty? 410 a = fnuchr%(x$) 420 if a = t then goto 700 ' Null repeat character? 430 q$=mid$(x$,2,3) ' Get the quadruplet to decode. 440 x$=mid$(x$,5) 450 b = fnuchr%(q$) 460 q$ = mid$(q$,2) 470 c = fnuchr%(q$) 480 q$ = mid$(q$,2) 490 d = fnuchr%(q$) 500 y$ = y$ + chr$(((a * 4) + (b \ 16)) and 255) ' Decode the quad. 510 y$ = y$ + chr$(((b * 16) + (c \ 4)) and 255) 520 y$ = y$ + chr$(((c * 64) + d) and 255) 530 goto 400 ' Get another quad. 700 x$ = mid$(x$,2) ' Expand the nulls. 710 r = fnuchr%(x$) ' Get the number of nulls. 715 print " Null: ",r 720 x$ = mid$(x$,2) 730 for i=1 to r ' Loop, adding nulls to string. 740 y$ = y$ + n$ 750 next 760 print#2,y$; ' Output the nulls to the file. 770 y$ = "" ' Clear the output buffer. 780 goto 400 800 print "Processing complete, elapsed time: "+t$+" to "+time$ 810 print "Output in "+f$ 820 close #1,#2 830 goto 9999 900 print "?The version of the MSKERMIT.BOO file is incorrect" 910 goto 820 9999 end //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 640 mspctran.bas /bin/echo -n ' '; /bin/ls -ld mspctran.bas fi /bin/echo 'Extracting msrbboo.bas' sed 's/^X//' <<'//go.sysin dd *' >msrbboo.bas 1 DEFINT A-Z:ZRUBOUT$=CHR$(8)+" "+CHR$(8):ZESCAPE$=CHR$(27):'Sreen utility definitions B.E. 2 ZLEADIN$=ZESCAPE$+"[":ZCLEAR$=ZLEADIN$+"J":ZHOME$=ZLEADIN$+"0;0H" 3 ZDOUBLE1$=ZESCAPE$+"#3":ZDOUBLE2$=ZESCAPE$+"#4":WIDTH 255 4 ZBOLD$=ZLEADIN$+"1m":ZBLINK$=ZLEADIN$+"5m":ZSAVE$=ZESCAPE$+"7" 5 ZREVERSE$=ZLEADIN$+"7m":ZOFF$=ZLEADIN$+"0m":ZREST$=ZESCAPE$+"8" 6 ZGRAPHON$=ZESCAPE$+"(0":ZGRAPHOFF$=ZESCAPE$+"(B":ZBACKER$=ZLEADIN$+"0K" 7 ZKEYPAD$=ZESCAPE$+"=":ZBELL$=CHR$(7):ZCLRLIN$=ZLEADIN$+"2K" 8 DEF FNXY$(ZX,ZY)=ZLEADIN$+MID$(STR$(INT(ZX)),2)+";"+MID$(STR$(INT(ZY)),2)+"H":'Cursor Adressing function (ZX=Line[1..24],ZY=Column[1..80]) 9 GOTO 25:'This to be modified to GOTO Start of program <=================== 10 ZSTRING$="":ZORGL=ZLENGTH:PRINT ZSAVE$+ZREVERSE$+STRING$(ZORGL,95)+ZOFF$+STRING$(ZORGL,8);:'General Input-GOSUB (Input:ZLENGTH, OUTPUT:ZLENGTH,ZSTRING,ZNUMBER,ZRANDOM) 11 ZTEMP$=INKEY$:ZRANDOM=(ZRANDOM MOD 2000)+1:IF LEN(ZTEMP$)=0 THEN 11'Wait for Char 12 IF ASC(ZTEMP$)=127 OR ASC(ZTEMP$)=8 THEN 17 ELSE IF ASC(ZTEMP$)=21 THEN PRINT ZREST$+ZBACKER$;:ZLENGTH=ZORGL:GOTO 10 ELSE PRINT ZTEMP$;'RUBOUT 13 IF ASC(ZTEMP$)=3 THEN GOTO 9999 ELSE IF ZTEMP$ >= "a" THEN ZTEMP$=CHR$(ASC(ZTEMP$)-32)'Uppercase Modify GOTO xx to Control-C intercept <===================== 14 IF ASC(ZTEMP$)=13 THEN PRINT:GOTO 16'RETURN finishes 15 ZSTRING$=ZSTRING$+ZTEMP$:ZLENGTH=ZLENGTH-1:IF ZLENGTH >0 THEN 11 16 ZLENGTH=LEN(ZSTRING$):ZNUMBER=VAL(ZSTRING$): RETURN 17 IF LEN(ZSTRING$)>0 THEN ZLENGTH=ZLENGTH +1:ZSTRING$=LEFT$(ZSTRING$,(LEN(ZSTRING$)-1)):PRINT ZRUBOUT$;:GOTO 11 ELSE PRINT ZBELL$;: GOTO 11'Cleanup after RUBOUT 18 'End of VT100 definitions ***** 19 'Use this BASIC program on the CP/M side of the Rainbow (with 20 'Microsoft MBasic-86) to translate the MSRB100.BOO file on 21 'your CP/M disk to binary .EXE format, then from the MS-DOS 22 'side use RDCPM to transfer the result to the MS-DOS file 23 'system. This program takes about 30 minutes to run on a Rainbow 24 'with floppy disks. 25 '- Bill Catchings, CU; modified for Rainbow by Bernie Eiben, DEC. 26 PRINT ZHOME$+ZCLEAR$;"Rainbow 4for3 Code Expander Version 1" 30 PRINT:PRINT: N$ = CHR$(0) 40 Z = ASC("0") 50 T = ASC("~")-Z 60 DEF FNUCHR%(A$)=ASC(A$)-Z 61 PRINT "FILE-NAME to Expand : ";:ZLENGTH=13:GOSUB 10:'Get Input 70 OPEN "I",1,ZSTRING$ 100 INPUT#1,F$ ' Is this the right file? 110 IF LEN(F$) > 20 THEN GOTO 900 120 OPEN "O",2,F$ ' Ouput-name from file 130 PRINT "Outputting to "+F$ 200 IF EOF(1) THEN GOTO 800 ' Exit nicely on end of file. 210 INPUT#1,X$ ' Get a line. 220 Y$ = "" ' Clear the output buffer. 230 GOTO 400 300 PRINT#2,Y$; ' Print output buffer to file. 310 GOTO 200 ' Get another line. 400 IF LEN(X$) < 4 GOTO 300 ' Is the input buffer empty? 410 A = FNUCHR%(X$) 420 IF A = T THEN GOTO 700 ' Null repeat character? 430 Q$=MID$(X$,2,3) ' Get the quadruplet to decode. 440 X$=MID$(X$,5) 450 B = FNUCHR%(Q$) 460 Q$ = MID$(Q$,2) 470 C = FNUCHR%(Q$) 480 Q$ = MID$(Q$,2) 490 D = FNUCHR%(Q$) 500 Y$ = Y$ + CHR$(((A * 4) + (B \ 16)) AND 255) ' Decode the quad. 510 Y$ = Y$ + CHR$(((B * 16) + (C \ 4)) AND 255) 520 Y$ = Y$ + CHR$(((C * 64) + D) AND 255) 530 GOTO 400 ' Get another quad. 700 X$ = MID$(X$,2) ' Expand the nulls. 710 R = FNUCHR%(X$) ' Get the number of nulls. 715 PRINT FNXY$(6,5)+ZCLRLIN$;" Null: ",R 720 X$ = MID$(X$,2) 730 FOR I=1 TO R ' Loop, adding nulls to string. 740 Y$ = Y$ + N$ 750 NEXT 760 PRINT#2,Y$; ' Output the nulls to the file. 770 Y$ = "" ' Clear the output buffer. 780 GOTO 400 800 PRINT "Processing complete" 810 PRINT "Output in "+F$ 820 CLOSE #1,#2 830 GOTO 9999 900 PRINT "?The FORMAT of the ",ZSTRING$," file is incorrect" 910 GOTO 820 9999 END //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 640 msrbboo.bas /bin/echo -n ' '; /bin/ls -ld msrbboo.bas fi /bin/echo 'Extracting msrbboo.hlp' sed 's/^X//' <<'//go.sysin dd *' >msrbboo.hlp Date: Thu 13 Sep 84 16:32:44-EDT From: Frank da Cruz <SY.FDC@CU20B.ARPA> Subject: Rainbow MS-DOS Kermit Bootstrapping To: Info-Kermit@CU20B Users of DEC Rainbow 100s have complained that there's no bootstrapping procedure they can use for getting the new MS-DOS Kermit onto the Rainbow over the communication line. The problem was that Basic was not available for MS-DOS on the Rainbow (or else it was so new that no one had it yet), so the Microsoft Basic program we provided for decoding the .BOO (4-for-3 encoded) binary file could not be used on the Rainbow. Now, thanks to Bernie Eiben at DEC, we have a version of the Basic program that will run on the CP/M-86 side of the Rainbow. It's a reworking of MSPCTRAN.BAS, which assumes that you already have the .BOO file on your CP/M disk. It builds an .EXE file, which you can then move to the MS-DOS side of your Rainbow by booting MS-DOS and then using RDCPM to get the file from the CP/M-format disk. How you get the .BOO file onto the CP/M disk in the first place is another question. Either you use some file capture utility -- commercial or otherwise -- or else you go through the DDT bootstrap procedure given for CP/M-80 Kermit (since the Rainbow is also a CP/M-80 system). Bernie's program is in KER:MSRBBOO.BAS. //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 640 msrbboo.hlp /bin/echo -n ' '; /bin/ls -ld msrbboo.hlp fi /bin/echo 'Extracting msrbemacs.ini' sed 's/^X//' <<'//go.sysin dd *' >msrbemacs.ini ; EMACS function key setup for Kermit-MS/Rainbow ; ; C-@ (set mark) on SELECT set key select \00 ; ; M-h (select region) on CTRL-SELECT set key scan 1313 \33h ; ; C-U 12 C-X C-I (rigidly indent region 12 spaces) on TAB set key scan 9 \25\61\62\30\11 ; C-X C-I (rigidly indent region) on SHIFT-TAB set key scan 521 \30\11 ; ; C-S (forward search) on FIND set key find \23 ; ; C-R (reverse search) on CTRL-FIND set key scan 1307 \22 ; ; M-D (delete word) on REMOVE set key remove \33d ; ; M-K (delete sentence) on CTRL-REMOVE set key scan 1311 \33k ; ; C-P (up line) on uparrow set key scan 295 \20 ; ; M-[ (up paragraph) on CTRL-uparrow set key scan 1319 \33[ ; ; C-X [ (up page) on SHIFT-uparrow set key scan 807 \30[ ; ; M-< (top of file) on CTRL-SHIFT-uparrow set key scan 1831 \33< ; ; C-B (back character) on leftarrow set key scan 301 \02 ; ; C-A (beginning of line) on CTRL-leftarrow set key scan 1325 \01 ; ; M-A (back sentence) on SHIFT-leftarrow set key scan 813 \33a ; ; C-N (next line) on downarrow set key scan 297 \16 ; M-] (down paragraph) on CTRL-downarrow set key scan 1321 \33] ; ; C-X ] (down page) on SHIFT-downarrow set key scan 809 \30] ; ; M-> (end of file) on CTRL-SHIFT-downarrow set key scan 1833 \30> ; ; C-F (forward character) on rightarrow set key scan 299 \06 ; ; C-E (end of line) on CTRL-rightarrow set key scan 1323 \05 ; ; M-E (end of sentence) on SHIFT-rightarrow set key scan 811 \33e ; ; C-X E (do keyboard macro) on DO set key scan 257 \30e ; ; C-U C-X E (do keyboard macro 4x) on CTRL-DO set key scan 1281 \25\30e ; ; C-U 8 C-X E (do keyboard macro 8x) on SHIFT-DO set key scan 769 \25\70\30e ; ; C-U C-U C-X E (do keyboard macro 16x) on CTRL-SHIFT-DO set key scan 1793 \25\25\30e ; ; ^_ on HELP set key scan 256 \37 ; ; C-X C-Z on EXIT set key scan 271 \30\32 //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 640 msrbemacs.ini /bin/echo -n ' '; /bin/ls -ld msrbemacs.ini fi /bin/echo 'Extracting msxapc.asm' sed 's/^X//' <<'//go.sysin dd *' >msxapc.asm ; Kermit system dependent module for NEC Advanced Personal Computer (APC) ; Ron Blanford, University of Washington, August 1984 public serini, serrst, clrbuf, outchr, coms, vts, dodel, public ctlu, cmblnk, locate, lclini, prtchr, dobaud, clearl, public dodisk, getbaud, beep, public count, xofsnt, puthlp, putmod, clrmod, poscur public sendbr, term, machnam, setktab, setkhlp, showkey include msdefs.h false equ 0 true equ 1 ; port assignments for 8251 serial controllers ; Standard interface mndata equ 30H ; Data port (read/write) mnst1a equ 32H ; Status port (when read) mncmda equ 32H ; Command port (when written) mnst2a equ 34H ; Alternate status port (when read) mnmska equ 34H ; Mask port (when written) mntdca equ 36H ; Transmit disable port (write only) ; Optional (H14) interface mndatb equ 31H ; Data port (read/write) mnst1b equ 33H ; Status port (when read) mncmdb equ 33H ; Command port (when written) mnst2b equ 35H ; Alternate status port (when read) mnmskb equ 35H ; Mask port (when written) mntdcb equ 37H ; Transmit disable port (write only) ; Status bits from mnst1 txrdy equ 01H ; Bit for output ready. rxrdy equ 02H ; Bit for input ready. ; Command values for mncmd ccmd equ 37H ; RTS & DTR high, RX & TX enabled, reset ERR cbrk equ 08H ; break enabled cmode equ 40H ; enable mode reset mmode equ 4EH ; 16x rate, 8 data, no parity, 1 stop ; Mask values for mnmsk txmsk equ 01H ; disables transmit ready interrupt rxmsk equ 02H ; disables receive ready interrupt tbemsk equ 04H ; disables transmit buffer empty interrupt ; port assignments for 8253 timers ; Standard interface tmdata equ 2BH ; data port tmcmda equ 27H ; command port ; Optional (H14) interface tmdatb equ 61H ; data port tmcmdb equ 67H ; command port ; values for tmcmd which select timer channel and mode tmsela equ 76H ; Channel 1, mode 3 (standard port) tmselb equ 36H ; Channel 0, mode 3 (optional port) ; Timer information for current port selection tmrinfo struc tmdat dw 0 ; data port tmcmd dw 0 ; command port tmsel db 0 ; byte which selects channel and mode tmrinfo ends ; port assignments for 8259 interrupt controllers ; Standard interface intcmda equ 20H ; Command port (master controller) intmska equ 22H ; Mask port ictmsk equ 08H ; Timer interrupt mask (to master) icsmska equ 02H ; Standard serial interrupt mask (to master) icsvcta equ 11H ; Interrupt vector for standard interface ; Optional (H14) interface ; The interrupt request vector for the optional (H14) serial interface is ; jumper-selectable to any of vectors IR2, IR5, IR8, or IR12. NEC recommends ; that IR8 be used, so that has been selected as the default here. To use ; any of the other vectors, set the following conditionals appropriately. ; Only one of the following should be true: IR2 equ false ; interrupt vector 2 IR5 equ false ; interrupt vector 5 IR8 equ true ; interrupt vector 8 IR12 equ false ; interrupt vector 12 IF IR2 intcmdb equ 20H ; Command port (master controller) intmskb equ 22H ; Mask port icsmskb equ 04H ; Interrupt mask icsvctb equ 12H ; Interrupt table index ENDIF IF IR5 intcmdb equ 20H ; Command port (master controller) intmskb equ 22H ; Mask port icsmskb equ 20H ; Interrupt mask icsvctb equ 15H ; Interrupt table index ENDIF IF IR8 intcmdb equ 28H ; Command port (slave controller) intmskb equ 2AH ; Mask port icsmskb equ 02H ; Interrupt mask icsvctb equ 19H ; Interrupt table index ENDIF IF IR12 intcmdb equ 28H ; Command port (slave controller) intmskb equ 2AH ; Mask port icsmskb equ 20H ; Interrupt mask icsvctb equ 1DH ; Interrupt table index ENDIF ; generic end of interrupt for intcmd icEOI equ 20H ; miscellaneous constants ctrlP equ 10H ; Key that toggles printer echo mntrgh equ bufsiz*3/4 ; High XON/XOFF trigger = 3/4 of buffer full. ; external variables used: ; drives - # of disk drives on system ; flags - global flags as per flginfo structure defined in pcdefs ; trans - global transmission parameters, trinfo struct defined in pcdefs ; portval - pointer to current portinfo structure (currently either port1 ; or port2) ; port1, port2 - portinfo structures for the corresponding ports ; global variables defined in this module: ; xofsnt, xofrcv - tell whether we saw or sent an xoff. datas segment public 'datas' extrn drives:byte,flags:byte, trans:byte extrn portval:word, port1:byte, port2:byte machnam db 'NEC APC$' nyimsg db cr,lf,'Not yet implemented$' badbd db cr,lf,'Unimplemented baud rate$' lstpos dw 0 ; column position for printer echoing crlf db cr,lf,'$' delstr db BS,' ',BS,'$' ; Delete string. clrlin db cr,'$' ; Clear line (just the cr part). ceolseq db esc,'[K$' ; Clear to end of line cpseq db esc,'=rc' ; rc replaced by row and column before display clrseq db 01EH,01AH,'$' ; Home cursor and clear screen ; The following color values were selected to look well on both monochrome ; and color monitors. In particular, the normal color should be at normal ; intensity on the monochrome monitor (green, blue, or cyan), and the bold ; color should be at bold intensity (red, purple, yellow, or white). nrmseq db esc,'[0m$' ; reset to normal video (green) invseq db esc,'[7m$' ; start reverse video (green) bldseq db esc,'[19m$' ; start bold video (purple) ourarg termarg <> modem mdminfo <mndata,mnst1a,mncmda,0,0,0,0> timer tmrinfo <tmdata,tmcmda,tmsela> ourflgs db 0 ; flags for telnet options fprint equ 80H ; echo screen output to printer oldsera dw ? ; old serial handler for standard port oldsega dw ? ; segment of above oldmska db ? ; old interrupt controller mask portina db 0 ; Has comm port been initialized. oldserb dw ? ; old serial handler for optional port oldsegb dw ? ; segment of same. oldmskb db ? ; old interrupt controller mask portinb db 0 ; Has comm port been initialized. xofsnt db 0 ; Say if we sent an XOFF. xofrcv db 0 ; Say if we received an XOFF. ; variables for serial interrupt handler source db bufsiz DUP (?) ; Buffer for data from port. srcpnt dw 0 ; Pointer in buffer (DI). count dw 0 ; Number of chars in int buffer. savesi dw 0 ; Save SI register here. dw 80 DUP (?) ; local stack for interrupt processing mnstk dw ? mnsp dw ? ; remote stack info mnsseg dw ? shkbuf db 300 dup (?) ; room to display key definition shkmsg db ' Scan code: ' shkmln equ $-shkmsg shkms1 db cr,lf,' Definition: ' shkm1ln equ $-shkms1 setktab db 2 mkeyw 'BACKSPACE',08H mkeyw 'SCAN',-1 setkhlp db cr,lf,'BACKSPACE, or SCAN followed by decimal ASCII code$' comptab db 7 mkeyw '1',1 mkeyw '2',0 mkeyw 'COM1',1 mkeyw 'COM2',0 mkeyw 'H14',0 mkeyw 'OPTIONAL',0 mkeyw 'STANDARD',1 bddat label word dw 0D30H ; 45.5 baud dw 0C00H ; 50 baud dw 0800H ; 75 baud dw 0574H ; 110 baud dw 0476H ; 134.5 baud dw 0400H ; 150 baud dw 0200H ; 300 baud dw 0100H ; 600 baud dw 0080H ; 1200 baud dw 0055H ; 1800 baud dw 004DH ; 2000 baud dw 0040H ; 2400 baud dw 0020H ; 4800 baud dw 0010H ; 9600 baud dw 0008H ; 19200 baud dw 0004H ; 38400 baud (not tested - may not work) datas ends code segment public extrn comnd:near, dopar:near assume cs:code,ds:datas ; local initialization routine, called by Kermit initialization. LCLINI PROC NEAR cld mov flags.vtflg,0 ; turn off heath emulation mov dx,offset nrmseq ; set to our normal background color call tmsg ret LCLINI ENDP ; this is called by Kermit initialization. It checks the ; number of disks on the system, sets the drives variable ; appropriately. The only problem is that a value of two ; is returned for single drive systems to be consistent ; with the idea of the system having logical drives A and ; B. Returns normally. DODISK PROC NEAR mov ah,gcurdsk ; current disk value to AL. int dos mov dl,al ; put current disk in DL. mov ah,seldsk ; select current disk. int dos ; get number of drives in AL. mov drives,al ret DODISK ENDP ; show the definition of a key. The terminal argument block (which contains ; the address and length of the definition tables) is passed in ax. ; Returns a string to print in AX, length of same in CX. ; Returns normally. ; On the APC there is no direct access to the keyboard; the best we ; can do is use direct console I/O to get a key value which has already ; been translated to some extent by the operating system. SHOWKEY PROC NEAR push es push ax ; save the terminal argument block mov bx,ds mov es,bx ; address data segment cld showk1: mov ah,dconio ; get scan value mov dx,0FFH int dos jz showk1 mov ah,0 push ax ; save scan code mov di,offset shkbuf ; move 'Scan code' message to buffer mov si,offset shkmsg mov cx,shkmln rep movsb call nout ; add scan code to buffer mov si,offset shkms1 ; move 'Definition' message to buffer mov cx,shkm1ln rep movsb pop ax ; retrieve scan code pop bx ; and terminal argument block mov cx,[bx].klen ; length of translation table jcxz showk3 ; no table, key not defined push di mov di,[bx].ktab ; get table address repne scasw ; look for scan code mov si,di pop di jne showk3 ; not defined sub si,[bx].ktab ; compute entry offset in table sub si,2 add si,[bx].krpl ; index to replacement mov si,[si] ; get its address mov cl,[si] ; get its length mov ch,0 inc si rep movsb ; transfer replacement to display buffer showk3: mov ax,offset shkbuf ; return address of buffer in ax mov cx,di ; and length in cx sub cx,ax pop es ret SHOWKEY ENDP ; copy numeric value from AX to ASCII buffer indicated by DI. DI is updated. NOUT PROC NEAR mov dx,0 ; zero high word mov bx,10 ; divide div bx push dx ; save remainder digit or ax,ax ; anything left? jz nout1 ; no, start output phase call nout nout1: pop ax ; retrieve a digit add al,'0' ; make it ASCII stosb ; put it in buffer ret NOUT ENDP ; skip returns if no character available at port, ; otherwise returns with char in al, # of chars in buffer in dx. PRTCHR PROC NEAR call chkxon ; see if we have to xon the host. cmp count,0 jnz prtch2 jmp rskp ; No data - check console. prtch2: pushf ; save current interrupt value cli ; disable interrupts while manipulating pointers mov si,savesi lodsb ; get a byte cmp si,offset source + bufsiz ; bigger than buffer? jb prtch1 ; no, keep going mov si,offset source ; yes, wrap around prtch1: dec count mov savesi,si mov dx,count ; return # of chars in buffer popf ; restore original interrupt flag ret PRTCHR ENDP ; local routine to see if we have to transmit an xon CHKXON PROC NEAR push bx mov bx,portval cmp [bx].floflg,0 ; doing flow control? je chkxo1 ; no, skip all this cmp xofsnt,false ; have we sent an xoff? je chkxo1 ; no, forget it cmp count,mntrgh ; below trigger? jae chkxo1 ; no, forget it mov ax,[bx].flowc ; ah gets xon call outchr ; send it nop ; ignore failure nop nop mov xofsnt,false ; remember we've sent an xon. chkxo1: pop bx ; restore register ret ; and return CHKXON ENDP ; Put the char in AH to the serial port. This assumes the ; port has been initialized. Should honor xon/xoff. Skip returns on ; success, returns normally if the character cannot be written. OUTCHR PROC NEAR mov bp,portval cmp ds:[bp].floflg,0 ; Are we doing flow control. je outch2 ; No, just continue. sub cx,cx ; clear counter outch1: cmp xofrcv,true ; Are we being held? jne outch2 ; No - it's OK to go on. loop outch1 ; held, try for a while mov xofrcv,false ; timed out, force it off and fall thru. outch2: push dx ; Save register. sub cx,cx mov al,ah ; Parity routine works on AL. call dopar ; Set parity appropriately. mov ah,al ; Don't overwrite character with status. mov dx,modem.mdstat ; port status register outch3: in al,dx test al,txrdy ; Transmitter ready? jnz outch4 ; Yes loop outch3 jmp outch5 ; Timeout outch4: mov al,ah ; Now send it out mov dx,modem.mddat out dx,al pop dx jmp rskp outch5: pop dx ret OUTCHR ENDP ; Send a break out the current serial port. Returns normally. SENDBR PROC NEAR mov dx,modem.mdcom ; send to command port mov al,cbrk+ccmd ; add break to normal command out dx,al sub cx,cx ; wait a while sndbr1: loop sndbr1 mov al,ccmd ; restore normal command out dx,al ret ; and return. SENDBR ENDP ; Clear the input buffer. This throws away all the characters in the ; serial interrupt buffer. This is particularly important when ; talking to servers, since NAKs can accumulate in the buffer. ; Returns normally. CLRBUF PROC NEAR pushf ; save current interrupt value cli ; disable interrupts mov ax,offset source ; reset pointers to beginning of buffer mov srcpnt,ax mov savesi,ax mov count,0 popf ; restore original interrupt value ret CLRBUF ENDP ; Set the baud rate for the current port, based on the value in the ; portinfo structure. On entry, previous value of baud rate is saved in AX. ; Returns normally. DOBAUD PROC NEAR mov bp,portval mov bx,ds:[bp].baud ;make sure new value is valid shl bx,1 add bx,offset bddat cmp word ptr [bx],0FFH jne dobd0 mov ds:[bp].baud,ax ;replace bad rate with previous value mov dx,offset badbd jmp tmsg dobd0: mov dx,timer.tmcmd ;timer command port mov al,timer.tmsel ;select proper channel and mode out dx,al mov ax,[bx] ;get timer initializer for this rate mov dx,timer.tmdat ;timer data port out dx,al ;output low byte mov al,ah out dx,al ;output high byte ret DOBAUD ENDP ; Get the current baud rate from the serial card and set it ; in the portinfo structure for the current port. Returns normally. ; This is used during initialization. GETBAUD PROC NEAR mov bx,portval ; no way to determine baud rate on APC mov [bx].baud,B1200 ; so set default baud rate to 1200 ret GETBAUD ENDP ; Set the mode for the current port. This is part of the serial ; initialization routine. DOMODE PROC NEAR mov dx,modem.mdcom ;send 3 zeros to command port to reset chip mov al,0 out dx,al mov al,0 out dx,al mov al,0 out dx,al mov al,cmode ;enable mode setting out dx,al push ax ;allow 8251 time to reset pop ax push ax pop ax mov al,mmode ;mode: 16x rate, 8 data, no parity, 1 stop out dx,al mov al,ccmd ;RTS & DTR high, RX & TX enabled, reset errors out dx,al ret DOMODE ENDP ; set the current port. COMS PROC NEAR mov dx,offset comptab ;get port selection mov bx,0 mov ah,cmkey call comnd jmp r push bx mov ah,cmcfm ;get a confirmation call comnd jmp comx nop pop bx mov flags.comflg,bl ;save port selection cmp flags.comflg,1 jne coms2 mov ax,offset port1 ;set to run on port 1 mov portval,ax call resetb ;reset port 2, if in use call inita ;set up port 1 ret coms2: mov ax,offset port2 ;set to run on port 2 mov portval,ax call reseta ;reset port 1, if in use call initb ;set up port 2 ret comx: pop bx ret COMS ENDP ; initialization for using serial port. This routine performs ; any initialization necessary for using the serial port, including ; setting up interrupt routines, setting buffer pointers, etc. ; Doing this twice in a row should be harmless (this version checks ; a flag and returns if initialization has already been done). ; SERRST below should restore any interrupt vectors that this changes. ; Returns normally. SERINI PROC NEAR cmp flags.comflg,1 jne seri2 call resetb call inita ret seri2: call reseta call initb ret SERINI ENDP ; Reset the serial port. This is the opposite of serini. Calling ; this twice without intervening calls to serini should be harmless. ; Returns normally. SERRST PROC NEAR call reseta ;reset port 1 call resetb ;reset port 2 ret SERRST ENDP ; Local routine to initialize the standard serial port INITA PROC NEAR cmp portina,1 ; Did we initialize port already? [21c] je inita0 ; Yes, so just leave. [21c] push es cli ; Disable interrupts mov ax,offset port1 mov portval,ax xor ax,ax ; Address low memory mov es,ax mov ax,es:[4*icsvcta] ; save standard port interrupt vector mov oldsera,ax mov ax,es:[4*icsvcta+2] mov oldsega,ax mov ax,offset serint ; point to our routine mov es:[4*icsvcta],ax ; point at our serial routine mov es:[4*icsvcta+2],cs ; our segment mov dx,intmska ; set up standard port... in al,dx mov oldmska,al ; save old master controller mask ; NEC recommends that the timer interrupt be disabled during interrupt- ; driven serial I/O, but this disables the clock display and keyboard ; repeat. I have not had any problems leaving it enabled, so I will ; leave it alone here. If problems develop, uncomment the following ; line to disable timer interrupts. -- RonB ; or al,ictmsk ; disable timer interrupt and al,not icsmska ; enable serial interrupt at master controller out dx,al mov dx,mnmska ; enable serial interrupt at port mov al,txmsk+tbemsk ; disable tx and tbe interrupts (enable rx) out dx,al mov dx,mntdca ; enable operation of serial port mov al,0 out dx,al mov modem.mddat,mndata mov modem.mdstat,mnst1a mov modem.mdcom,mncmda mov timer.tmdat,tmdata mov timer.tmcmd,tmcmda mov timer.tmsel,tmsela call domode call dobaud mov portina,1 ; Remember port has been initialized. call clrbuf ; Clear input buffer. sti ; Allow interrupts pop es inita0: ret INITA ENDP ; Local routine to initialize the optional (H14) serial port INITB PROC NEAR cmp portinb,1 ; Did we initialize port already? [21c] je initb0 ; Yes, so just leave. [21c] push es cli ; Disable interrupts mov ax,offset port2 mov portval,ax xor ax,ax ; Address low memory mov es,ax mov ax,es:[4*icsvctb] ; save optional port interrupt vector mov oldserb,ax mov ax,es:[4*icsvctb+2] mov oldsegb,ax mov ax,offset serint ; point to our routine mov es:[4*icsvctb],ax ; point at our serial routine mov es:[4*icsvctb+2],cs ; our segment mov dx,intmskb ; set up optional port... in al,dx mov oldmskb,al ; save old master or slave controller mask and al,not icsmskb ; enable serial interrupt at controller out dx,al mov dx,mnmskb ; enable serial interrupt at port mov al,txmsk+tbemsk ; disable tx and tbe interrupts (enable rx) out dx,al mov dx,mntdcb ; enable operation of serial port mov al,0 out dx,al mov modem.mdstat,mnst1b mov modem.mddat,mndatb mov modem.mdcom,mncmdb mov timer.tmdat,tmdatb mov timer.tmcmd,tmcmdb mov timer.tmsel,tmselb call domode call dobaud mov portinb,1 ; Remember port has been initialized. call clrbuf ; Clear input buffer. sti ; Allow interrupts pop es initb0: ret INITB ENDP ; Reset standard serial port RESETA PROC NEAR cmp portina,0 ; Did we reset port already? je rsta0 ; Yes, so just leave. push es cli ; Disable interrupts xor ax,ax ; Address low memory mov es,ax mov ax,oldsera ; Restore interrupt vector mov es:[4*icsvcta],ax mov ax,oldsega mov es:[4*icsvcta+2],ax mov dx,intmska ; restore old master controller mask mov al,oldmska out dx,al mov dx,mnmska ; disable serial interrupts at port mov al,txmsk+rxmsk+tbemsk out dx,al mov portina,0 ; Remember port has been reset sti ; Allow interrupts pop es rsta0: ret RESETA ENDP ; Reset optional (H14) serial port RESETB PROC NEAR cmp portinb,0 ; Did we reset port already? je rstb0 ; Yes, so just leave. push es cli ; Disable interrupts xor ax,ax ; Address low memory mov es,ax mov ax,oldserb ; Restore interrupt vector mov es:[4*icsvctb],ax mov ax,oldsegb mov es:[4*icsvctb+2],ax mov dx,intmskb ; restore old slave controller mask mov al,oldmskb out dx,al mov dx,mnmskb ; disable serial interrupts at port mov al,txmsk+rxmsk+tbemsk out dx,al mov portinb,0 ; Remember port has been reset sti ; Allow interrupts pop es rstb0: ret RESETB ENDP ; serial port interrupt routine. This is not accessible outside this ; module, handles serial port receiver interrupts. SERINT PROC NEAR push ds ; save these on remote stack push ax mov ax,seg datas ; get our own data segment mov ds,ax mov mnsp,sp ; save remote stack information mov mnsseg,ss mov sp,offset mnstk ; switch to local stack mov ss,ax push es ; and save remaining registers push bp push di push si push dx push cx push bx mov es,ax call mnproc ; process the interrupt mov al,icEOI cmp flags.comflg,1 ; If using standard port je intr1 mov dx,intcmdb ; or H14 vectored to master cmp dx,intcmda je intr1 ; only signal End of Interrupt to master, out dx,al ; otherwise signal to both slave and master. intr1: mov dx,intcmda out dx,al pop bx ; restore registers from stack pop cx pop dx pop si pop di pop bp pop es mov ax,mnsseg ; switch back to remote stack mov ss,ax mov ax,mnsp mov sp,ax pop ax pop ds iret ; handler for serial input mnproc: cld mov di,srcpnt ; get buffer pointer mov dx,modem.mdstat ; is data available? in al,dx test al,rxrdy jz mnpro7 mov dx,modem.mddat ; read data in al,dx or al,al jz mnpro7 ; Ignore nulls. cmp al,7FH ; Ignore rubouts, too. jz mnpro7 mov ah,al and ah,7fH ; only consider low-order 7 bits for flow ctl. mov bp,portval cmp ds:[bp].floflg,0 ; Doing flow control? je mnpro4 ; Nope. mov bx,ds:[bp].flowc ; Flow control char (BH = XON, BL = XOFF). cmp ah,bl ; Is it an XOFF? jne mnpro3 ; Nope, go on. mov xofrcv,true ; Set the flag. jmp short mnpro7 mnpro3: cmp ah,bh ; Get an XON? jne mnpro4 ; No, go on. mov xofrcv,false ; Clear our flag. jmp mnpro7 mnpro4: stosb cmp di,offset source + bufsiz jb mnpro5 ; not past end... mov di,offset source ; wrap buffer around mnpro5: mov srcpnt,di ; update ptr inc count cmp ds:[bp].floflg,0 ; Doing flow control? je mnpro7 ; No, just leave. cmp xofsnt,true ; Have we sent an XOFF? je mnpro7 ; Yes. cmp count,mntrgh ; Past the high trigger point? jbe mnpro7 ; No, we're within our limit. mov ah,bl ; Get the XOFF. call outchr ; Send it. nop ; ignore failure. nop nop mov xofsnt,true ; Remember we sent it. mnpro7: ret SERINT ENDP ; Dumb terminal emulator. Anyone wishing to enhance it is encouraged ; to do so. TERM PROC NEAR mov si,ax ; save argument block locally mov di,offset ourarg mov ax,ds mov es,ax mov cx,size termarg rep movsb term1: call prtchr ; Serial port input processor jmp short term2 ; ...have a char nop jmp termk ; no char, continue term2: and al,7FH ; only use ASCII in terminal mode push ax mov dl,al mov ah,conout int dos ; display char pop ax test ourarg.flgs,capt ; are we capturing output? jz term3 push ax call ourarg.captr pop ax term3: test ourflgs,fprint ; are we echoing to printer? jz termk call lstchr termk: mov ah,dconio ; Keyboard input processor mov dl,0FFH int dos ; check console jz term1 ; no char, continue cmp al,ourarg.escc ; is it the escape char? je termx cmp al,ctrlP ; is it the print toggle? jne term6 xor ourflgs,fprint jmp term1 term6: call trnout ; translate key and send it out jmp term1 termx: ret ; do appropriate translations on input key, and transmit trnout: mov ah,0 test ourarg.flgs,havtt ; is there a translation table? jz trnou2 mov cx,ourarg.klen ; get table length and origin mov di,ourarg.ktab repne scasw ; look for key jne trnou2 ; if not found, just send it sub di,ourarg.ktab ; reset to offset of replacement sub di,2 add di,ourarg.krpl mov si,[di] mov cl,[si] ; get length of replacement mov ch,0 jcxz trnou3 ; if length is zero, send nothing inc si trnou1: lodsb ; get replacement character push si push cx call sndhst ; send it to port pop cx pop si loop trnou1 ; continue until translation complete ret trnou2: call sndhst ; plain characters go out as they are trnou3: ret ; send character in AL to port, with possible local echo sndhst: push ax mov ah,al call outchr ; send char to port nop ; ...don't care if it fails nop nop pop ax test ourarg.flgs,lclecho ; doing local echo? jz sndhs2 mov dl,al mov ah,conout int dos ; if so, display char sndhs2: ret ; send character to printer. The only special case is the tab, which must ; be expanded to spaces because MS-DOS doesn't. lstchr: cmp al,tab jne lstch2 mov ax,lstpos ; current column position mov cx,8 ; # of spaces = 8 - (column % 8) div cl sub cl,ah add lstpos,cx ; update the column position mov al,' ' lstch1: call lstch4 ; print all the spaces loop lstch1 ret lstch2: cmp al,cr ; CR returns column count to zero jne lstch3 mov lstpos,0 lstch3: cmp al,' ' ; only printable characters are counted jb lstch4 cmp al,del je lstch4 inc lstpos lstch4: mov dl,al ; print the character in any case mov ah,lstout int dos ret TERM ENDP ; Set heath emulation on/off. VTS PROC NEAR mov dx,offset nyimsg jmp tmsg VTS ENDP ; Position the cursor according to contents of DX: ; DH contains row, DL contains column. Returns normally. POSCUR PROC NEAR push si cmp dh,25 ; out of range just assumes high value jb poscu1 mov dh,24 poscu1: cmp dl,80 jb poscu2 mov dl,79 poscu2: add dx,2020H ; add offset for ADM cursor addressing mov cpseq+2,dh mov cpseq+3,dl mov si,offset cpseq ; print sequence (ESC=rc) mov cx,4 posc1: lodsb mov dl,al mov ah,conout int dos loop posc1 pop si ret POSCUR ENDP ; Locate; homes the cursor. Returns normally. LOCATE PROC NEAR mov dx,0 ; Go to top left corner of screen. jmp poscur LOCATE ENDP ; Delete a character from the terminal. This works by printing ; backspaces and spaces. Returns normally. DODEL PROC NEAR cmp al,del ; Del character needs extra backspace jne dodel1 mov dl,bs mov ah,conout int dos dodel1: mov dx,offset delstr ; Erase weird character. jmp tmsg DODEL ENDP ; Move the cursor to the left margin, then clear to end of line. ; Returns normally. CTLU PROC NEAR mov dx,offset clrlin ; this just goes to left margin... call tmsg jmp clearl ; now clear line CTLU ENDP ; Clear to the end of the current line. Returns normally. CLEARL PROC NEAR mov dx,offset ceolseq ; clear sequence jmp tmsg CLEARL ENDP ; This routine blanks the screen. Returns normally. CMBLNK PROC NEAR mov dx,offset clrseq ; clear screen sequence jmp tmsg CMBLNK ENDP ; write a line in inverse video at the bottom of the screen... ; the line is passed in dx, terminated by a $. Returns normally. PUTMOD PROC NEAR push dx ; preserve message mov dx,24*100H ; line 24 call poscur mov dx,offset invseq ; put into inverse video call tmsg pop dx ; print the message call tmsg mov dx,offset nrmseq ; normal video jmp tmsg PUTMOD ENDP ; clear the mode line written by putmod. Returns normally. CLRMOD PROC NEAR mov dx,24*100H call poscur jmp clearl CLRMOD ENDP ; Put a help message on the screen. This one uses bold video... ; pass the message in ax, terminated by a null. Returns normally. PUTHLP PROC NEAR push ax ; save pointer to message mov dx,offset crlf call tmsg mov dx,offset bldseq ; set to bold video call tmsg pop si ; retrieve pointer to message puth1: lodsb ; get a character cmp al,0 je puth2 ; stop if terminator mov dl,al ; otherwise display the character mov ah,conout int dos jmp puth1 puth2: mov dx,offset nrmseq ; reset to normal video call tmsg mov dx,offset crlf call tmsg ret PUTHLP ENDP ; Produce a short beep. Returns normally. BEEP PROC NEAR mov dl,bell mov ah,conout int dos ret BEEP ENDP ; Prints $-terminated message in dx, for local use only TMSG PROC NEAR mov ah,prstr int dos ret TMSG 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 //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 640 msxapc.asm /bin/echo -n ' '; /bin/ls -ld msxapc.asm fi /bin/echo 'Extracting msxdmb.asm' sed 's/^X//' <<'//go.sysin dd *' >msxdmb.asm code segment public code ends datas segment public 'datas' datas ends stack segment stack 'stack' stack ends end //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 640 msxdmb.asm /bin/echo -n ' '; /bin/ls -ld msxdmb.asm fi /bin/echo 'Extracting msxdmb.hlp' sed 's/^X//' <<'//go.sysin dd *' >msxdmb.hlp MSXDMB.ASM is a dummy file to make the segments come out in the right order on the Rainbow version of Kermit-MS. It must be included with the other modules, as the first one. //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 640 msxdmb.hlp /bin/echo -n ' '; /bin/ls -ld msxdmb.hlp fi /bin/echo 'Extracting msxgen.asm' sed 's/^X//' <<'//go.sysin dd *' >msxgen.asm ; Generic MS DOS Kermit module public serini, serrst, clrbuf, outchr, coms, vts, dodel, public ctlu, cmblnk, locate, lclini, prtchr, dobaud, clearl, public dodisk, getbaud, beep public count, xofsnt, puthlp, putmod, clrmod, poscur public sendbr, term, machnam, setktab, setkhlp, showkey include msdefs.h false equ 0 true equ 1 instat equ 6 rddev equ 3fH open equ 3dH ; external variables used: ; drives - # of disk drives on system ; flags - global flags as per flginfo structure defined in pcdefs ; trans - global transmission parameters, trinfo struct defined in pcdefs ; portval - pointer to current portinfo structure (currently either port1 ; or port2) ; port1, port2 - portinfo structures for the corresponding ports ; global variables defined in this module: ; xofsnt, xofrcv - tell whether we saw or sent an xoff. datas segment public 'datas' extrn drives:byte,flags:byte, trans:byte extrn portval:word, port1:byte, port2:byte machnam db 'Generic MS-DOS 2.0$' erms20 db cr,lf,'?Warning: System has no disk drives$' ; [21a] erms40 db cr,lf,'?Warning: Unrecognized baud rate$' erms41 db cr,lf,'?Warning: Cannot open com port$' erms50 db cr,lf,'Error reading from device$' hnd1 db cr,lf,'Enter a file handle. Check your DOS manual if you are ' db cr,lf,'not certain what value to supply (generally 3).$' hnd2 db cr,lf,'Handle: $' hnderr db cr,lf,'Warning: Handle not known. Any routine using the ' db cr,lf,'communications port will probably not work.$' hndhlp db cr,lf,'A four digit file handle $' badbd db cr,lf,'Unimplemented baud rate$' noimp db cr,lf,'Command not implemented.$' shkmsg db 'Not implemented.' shklen equ $-shkmsg setktab db 0 setkhlp db 0 crlf db cr,lf,'$' delstr db BS,BS,' ',BS,BS,'$' ; Delete string. [21d] clrlin db cr,'$' ; Clear line (just the cr part). clreol db '^U',cr,lf,'$' ; Clear line. telflg db 0 ; non-zero if we're a terminal. xofsnt db 0 ; Say if we sent an XOFF. xofrcv db 0 ; Say if we received an XOFF. count dw 0 ; Number of chars in int buffer. prthnd dw 0 ; Port handle. prttab dw com2,com1 com1 db 'COM1',0 com2 db 'COM2',0 tmp db ?,'$' temp dw 0 temp1 dw ? ; Temporary storage. temp2 dw ? ; Temporary storage. rdbuf db 20 dup(?) ; Buffer for input. ; Entries for choosing communications port. [19b] comptab db 04H db 01H,'1$' dw 01H db 01H,'2$' dw 00H db 04H,'COM1$' dw 01H db 04H,'COM2$' dw 00H ourarg termarg <> datas ends code segment public extrn comnd:near, dopar:near, prserr:near, atoi:near, prompt:near assume cs:code,ds:datas ; this is called by Kermit initialization. It checks the ; number of disks on the system, sets the drives variable ; appropriately. Returns normally. DODISK PROC NEAR mov ah,gcurdsk ; Current disk value to AL. int dos mov dl,al ; Put current disk in DL. mov ah,seldsk ; Select current disk. int dos ; Get number of drives in AL. mov drives,al ret DODISK ENDP ; Clear the input buffer. This throws away all the characters in the ; serial interrupt buffer. This is particularly important when ; talking to servers, since NAKs can accumulate in the buffer. ; Do nothing since we are not interrupt driven. Returns normally. CLRBUF PROC NEAR ret CLRBUF ENDP ; Clear to the end of the current line. Returns normally. CLEARL PROC NEAR mov ah,prstr mov dx,offset clreol int dos ret CLEARL ENDP ; Put the char in AH to the serial port. This assumes the ; port has been initialized. Should honor xon/xoff. Skip returns on ; success, returns normally if the character cannot be written. outchr: mov bp,portval cmp ds:[bp].floflg,0 ; Are we doing flow control. je outch2 ; No, just continue. xor cx,cx ; clear counter outch1: cmp xofrcv,true ; Are we being held? jne outch2 ; No - it's OK to go on. loop outch1 ; held, try for a while mov xofrcv,false ; timed out, force it off and fall thru. outch2: push dx ; Save register. mov al,ah ; Parity routine works on AL. call dopar ; Set parity appropriately. mov dl,al mov ah,punout ; Output char in DL to comm port. int dos pop dx jmp rskp ; This routine blanks the screen. Returns normally. CMBLNK PROC NEAR mov ah,prstr mov dx,offset crlf ; Can't do anything else. int dos ret CMBLNK ENDP ; Homes the cursor. Returns normally. LOCATE PROC NEAR mov dx,0 ; Go to top left corner of screen. jmp poscur LOCATE ENDP ; Write a line at the bottom of the screen... ; the line is passed in dx, terminated by a $. Returns normally. putmod proc near push dx ; preserve message mov dx,1800h ; now address line 24 call poscur pop dx ; get message back mov ah,prstr int dos ; write it out ret ; and return putmod endp ; clear the mode line written by putmod. Returns normally. clrmod proc near mov dx,1800h call poscur ; Go to bottom row. call clearl ; Clear to end of line. ret clrmod endp ; Put a help message on the screen. ; Pass the message in ax, terminated by a null. Returns normally. puthlp proc near push ax ; preserve this mov ah,prstr mov dx,offset crlf int dos pop si ; point to string again puthl3: lodsb ; get a byte cmp al,0 ; end of string? je puthl4 ; yes, stop mov dl,al mov ah,dconio int dos ; else write to screen jmp puthl3 ; and keep going puthl4: mov ah,prstr mov dx,offset crlf int dos ret puthlp endp ; Set the baud rate for the current port, based on the value ; in the portinfo structure. Returns normally. DOBAUD PROC NEAR mov ah,prstr mov dx,offset noimp ; Say it's not implemented. int dos mov bx,portval mov [bx].baud,0FFFFH ; So it's not a recognized value. ret ; Must be set before starting Kermit. DOBAUD ENDP ; Get the current baud rate from the serial card and set it ; in the portinfo structure for the current port. Returns normally. ; This is used during initialization. GETBAUD PROC NEAR ret ; Can't do this. GETBAUD ENDP ; Use for DOS 2.0 and above. Check the port status. If no data, skip ; return. Else, read in a char and return. PRTCHR PROC NEAR push bx push cx push si push bp call chkxon mov bx,prthnd mov al,instat mov ah,ioctl int dos or al,al jz prtch4 ; not ready... mov bx,prthnd mov ah,rddev mov cx,1 mov dx,offset temp int dos cmp al,5 ; Error condition. je prt3x cmp al,6 ; Error condition je prt3x mov al,byte ptr temp mov bp,portval cmp ds:[bp].parflg,PARNON ; no parity? je prtch3 ; then don't strip and al,7fh ; else turn off parity prtch3: pop bp pop si pop cx pop bx ret prt3x: mov ah,prstr mov dx,offset erms50 int dos prtch4: pop bp pop si pop cx pop bx jmp rskp ; no chars... PRTCHR ENDP ; Local routine to see if we have to transmit an xon chkxon proc near push bx mov bx,portval cmp [bx].floflg,0 ; doing flow control? je chkxo1 ; no, skip all this cmp xofsnt,false ; have we sent an xoff? je chkxo1 ; no, forget it mov ax,[bx].flowc ; ah gets xon call outchr ; send it nop nop nop ; in case it skips mov xofsnt,false ; remember we've sent the xon. chkxo1: pop bx ; restore register ret ; and return chkxon endp ; Send a break out the current serial port. Returns normally. SENDBR PROC NEAR ret SENDBR ENDP ; Position the cursor according to contents of DX: ; DH contains row, DL contains column. Returns normally. POSCUR PROC NEAR ret POSCUR ENDP ; Delete a character from the terminal. This works by printing ; backspaces and spaces. Returns normally. DODEL PROC NEAR mov ah,prstr mov dx,offset delstr ; Erase weird character. int dos ret DODEL ENDP ; Move the cursor to the left margin, then clear to end of line. ; Returns normally. CTLU PROC NEAR mov ah,prstr mov dx,offset clrlin int dos call clearl ret CTLU ENDP ; Set the current port. COMS PROC NEAR mov dx,offset comptab mov bx,0 mov ah,cmkey call comnd jmp r push bx mov ah,cmcfm call comnd ; Get a confirm. jmp comx ; Didn't get a confirm. nop pop bx mov flags.comflg,bl ; Set the comm port flag. cmp flags.comflg,1 ; Using Com 1? jne coms0 ; Nope. mov ax,offset port1 mov portval,ax ret coms0: mov ax,offset port2 mov portval,ax ret comx: pop bx ret COMS ENDP ; Set heath emulation on/off. VTS PROC NEAR jmp notimp VTS ENDP notimp: mov ah,prstr mov dx,offset noimp int dos jmp prserr ; Initialize variables to values used by the generic MS DOS version. lclini: mov flags.vtflg,0 ; Don't to terminal emulation. call opnprt ; Get file handle for comm port. ret ; Get a file handle for the communications port. Use DOS call to get the ; next available handle. If it fails, ask user what value to use (there ; should be a predefined handle for the port, generally 3). The open ; will fail if the system uses names other than "COM1" or "COM2". opnprt: mov al,flags.comflg mov ah,0 mov si,ax shl si,1 ; double index mov dx,prttab[si] mov ah,open mov al,2 int dos jnc opnpr2 mov ah,prstr ; It didn't like the string. mov dx,offset erms41 int dos mov dx,offset hnd1 int dos opnpr0: mov dx,offset hnd2 ; Ask user to supply the handle. call prompt mov ah,cmtxt mov bx,offset rdbuf ; Where to put input. mov dx,offset hndhlp ; In case user wants help. call comnd jmp opnpr3 ; Maybe user typed a ^C. nop mov si,offset rdbuf call atoi ; Convert to real number jmp opnpr0 ; Keep trying. nop mov prthnd,ax ; Value returned in AX ret opnpr2: mov prthnd,ax ; Call succeeded. ret opnpr3: cmp flags.cxzflg,'C' ; Did user type a ^C? jne opnpr4 ; No, don't say anything. mov ah,prstr ; Else, issue a warning. mov dx,offset hnderr int dos opnpr4: ret ; Yes, fail. showkey: mov ax,offset shkmsg mov cx,shklen ret ; Initialization for using serial port. Returns normally. SERINI PROC NEAR cld ; Do increments in string operations call clrbuf ; Clear input buffer. ret ; We're done. SERINI ENDP ; Reset the serial port. This is the opposite of serini. Calling ; this twice without intervening calls to serini should be harmless. ; Returns normally. SERRST PROC NEAR ret ; All done. SERRST ENDP ; Produce a short beep. The PC DOS bell is long enough to cause a loss ; of data at the port. Returns normally. BEEP PROC NEAR mov dl,bell mov ah,dconio int dos ret BEEP ENDP ; Dumb terminal emulator. Doesn't work too well above 1200 baud (and ; even at 1200 baud you sometimes lose the first one or two characters ; on a line). term proc near mov si,ax ; this is source mov di,offset ourarg ; place to store arguments mov ax,ds mov es,ax ; address destination segment mov cx,size termarg rep movsb ; copy into our arg blk term1: call prtchr jmp short term2 ; have a char... nop nop jmp short term3 ; no char, go on term2: push ax and al,7fh ; mask off parity for terminal mov dl,al mov ah,conout int dos ; go print it pop ax test ourarg.flgs,capt ; capturing output? jz term3 ; no, forget it call ourarg.captr ; else call the routine term3: mov ah,dconio mov dl,0ffh int dos jz term1 ; no character, go on cmp al,ourarg.escc ; escape char? je term4 ; yes, exit push ax ; save char mov ah,al or ah,80H ; turn on hi bit so DOS doesn't interfere call outchr ; output the character nop nop nop pop ax test ourarg.flgs,lclecho ; echoing? jz term1 ; no, continue loop mov dl,al mov ah,dconio int dos jmp term1 ; else echo and keep going term4: ret term 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//go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 640 msxgen.asm /bin/echo -n ' '; /bin/ls -ld msxgen.asm fi
knutson@ut-ngp.UUCP (Jim Knutson) (10/05/84)
: Run this shell script with "sh" not "csh" PATH=:/bin:/usr/bin:/usr/ucb export PATH all=FALSE if [ $1x = -ax ]; then all=TRUE fi /bin/echo 'Extracting msxhp150.asm' sed 's/^X//' <<'//go.sysin dd *' >msxhp150.asm public serini, serrst, clrbuf, outchr, coms, vts, dodel, ctlu public cmblnk, locate, prtchr, dobaud, clearl, lclini public dodisk, getbaud, beep, setkhlp, setktab public machnam, xofsnt, count, term, poscur public clrmod, putmod, puthlp, sendbr, showkey include msdefs.h false equ 0 true equ 1 wrdev equ 40H rddev equ 3fH open equ 3dH close equ 3eH rdchan equ 2 e_send_break equ 6 e_ioctl equ 44h ; MSODS io control fct datas segment public 'datas' extrn drives:byte, flags:byte, trans:byte extrn portval:word, port1:byte, port2:byte machnam db 'HP-150$' erms20 db cr,lf,'?Warning: System has no disk drives$' ; [21a] erms40 db cr,lf,'?Warning: Unrecognized baud rate$' erms41 db cr,lf,'?Warning: Cannot open com port$' noimp db cr,lf,'Command not implemented.$' setktab db 0 setkhlp db 0 shkmsg db 'Not implemented.' shklen equ $-shkmsg crlf db cr,lf,'$' comphlp db cr,lf,'1 (COM1) 2 (COM2)$' ; [19b] delstr db BS,BS,' ',BS,BS,'$' ; Delete string. [21d] clrlin db cr,esc,'K$' xofsnt db 0 ; Say if we sent an XOFF. xofrcv db 0 ; Say if we received an XOFF. invseq db esc,'&dB$' ; Reverse video. nrmseq db esc,'&d@$' ; Normal mode. ivlseq db 80 dup (' '),cr,'$' ; Make a line inverse video tmp db ?,'$' temp dw 0 temp1 dw ? ; Temporary storage. temp2 dw ? ; Temporary storage. ; Entries for choosing communications port. [19b] comptab db 04H db 01H,'1$' dw 01H db 01H,'2$' dw 00H db 04H,'COM1$' dw 01H db 04H,'COM2$' dw 00H ; variables for serial interrupt handler source db bufsiz DUP(?) ; Buffer for data from port. bufout dw 0 ; buffer removal ptr count dw 0 ; Number of chars in int buffer. bufin dw 0 ; buffer insertion ptr telflg db 0 ; Are we acting as a terminal. [16] [17c] clreol db esc,'K$' prttab dw com2,com1 com1 db 'COM1',0 com2 db 'COM2',0 blank db esc,'H',esc,'J$' movcur db esc,'&a' colno db 20 dup (?) ten db 10 prthnd dw 0 tempbuf dw 10 dup(?) ourarg termarg <> datas ends code segment public extrn comnd:near, dopar:near, prserr:near assume cs:code,ds:datas ; See how many disk drives we have. DODISK PROC NEAR mov ah,gcurdsk ; Current disk value to AL. int dos mov dl,al ; Put current disk in DL. mov ah,seldsk ; Select current disk. int dos ; Get number of drives in AL. mov drives,al ret DODISK ENDP ; Clear the input buffer before sending a packet. [20e] CLRBUF PROC NEAR cli mov ax,offset source mov bufin,ax mov bufout,ax mov count,0 sti clrb1: call prtchr ; get a character jmp clrb1 ; until there aren't any more nop ret CLRBUF ENDP ; Common routine to clear to end-of-line. [19a] CLEARL PROC NEAR mov dx,offset clreol mov ah,prstr int dos ret CLEARL ENDP dobaud proc near jmp notimp dobaud endp ; Send the break signal out data comm. sendbr: mov al,e_send_break jmp dc_ioctl ; Set some data comm ioctl option. AL has function code. dc_ioctl proc near mov ah,8h mov tempbuf,ax mov dx,offset tempbuf mov ah,e_ioctl mov al,3 mov bx,prthnd mov cx,2 int 21h ret dc_ioctl endp outchr: mov bp,portval cmp ds:[bp].floflg,0 ; Are we doing flow control. je outch2 ; No, just continue. xor cx,cx ; clear counter outch1: cmp xofrcv,true ; Are we being held? jne outch2 ; No - it's OK to go on. loop outch1 ; held, try for a while mov xofrcv,false ; timed out, force it off and fall thru. outch2: push dx ; Save register. push cx push bx cmp prthnd,0 ; do we have a port handle? jne outch3 ; yes, go on push ax call opnprt ; open the port pop ax outch3: mov byte ptr temp,ah ; save character mov bx,prthnd mov ah,wrdev mov cx,1 mov dx,offset temp int dos pop bx pop cx pop dx jmp rskp opnprt: mov al,flags.comflg mov ah,0 mov si,ax shl si,1 ; double index mov dx,prttab[si] mov ah,open mov al,2 int dos jnc opnpr1 mov ah,prstr mov dx,offset erms41 int dos ret opnpr1: mov prthnd,ax ret ; This routine blanks the screen. CMBLNK PROC NEAR ; This is stolen from the IBM example. mov ah,prstr mov dx,offset blank int dos ret CMBLNK ENDP LOCATE PROC NEAR mov dx,0 ; Go to top left corner of screen. jmp poscur ; callret... LOCATE ENDP GETBAUD PROC NEAR ret GETBAUD ENDP ; skip returns if no character available at port, ; otherwise returns with char in al, # of chars in buffer in dx. PRTCHR PROC NEAR push bx push cx push si push bp cmp count,0 ; no characters? jne prtch2 ; no, go fill buffer cmp prthnd,0 ; have a handle yet? jne prtch1 ; yes, keep going call opnprt prtch1: mov bx,prthnd mov al,rdchan mov ah,ioctl mov dx,offset source ; buffer to read into mov cx,bufsiz ; length of buffer int dos mov count,ax ; reset count or ax,ax jz prtch4 ; still no chars mov bufout,offset source ; this is output ptr prtch2: dec count mov dx,count ; return count in dx mov si,bufout lodsb ; get character mov bufout,si ; update ptr mov bp,portval cmp ds:[bp].parflg,PARNON ; no parity? je prtch3 ; then don't strip and al,7fh ; else turn off parity prtch3: pop bp pop si pop cx pop bx ret prtch4: pop bp pop si pop cx pop bx jmp rskp ; no chars... PRTCHR ENDP ; Position the cursor according to contents of DX. POSCUR PROC NEAR mov ax,ds mov es,ax ; address data segment!!! cld mov di,offset colno mov al,dl ; column call nout mov al,'c' stosb mov al,dh ; row call nout mov al,'Y' stosb mov al,'$' stosb mov dx,offset movcur mov ah,prstr int dos ; print the sequence ret POSCUR ENDP NOUT PROC NEAR cbw ; extend to word div byte ptr ten ; divide by 10 or al,al ; any quotient? jz nout1 ; no, forget this push ax ; save current result call nout ; output high order pop ax ; restore nout1: mov al,ah ; get digit add al,'0' ; make printable stosb ret ; put in buffer and return NOUT endp ; Write a line in inverse video at the bottom of the screen... ; the line is passed in dx, terminated by a $. Returns normally. putmod proc near push dx ; preserve message mov dx,24 * 100H ; line 24 call poscur mov dx,offset invseq ; put into inverse video mov ah,prstr int dos pop dx int dos mov dx,offset nrmseq ; normal videw int dos ret ; and return putmod endp ; Clear the mode line written by putmod. Returns normally. clrmod proc near mov dx,24 * 100H call poscur call clearl ret clrmod endp ; Put a help message one the screen in reverse video. Pass ; the message in AX, terminated by a null. Returns normally. ; The message is put wherever the cursor currently is located. puthlp proc near push ax mov ah,prstr ; Leave some room before the message. mov dx,offset crlf int dos pop si ; Put message address here. puth0: mov ah,prstr mov dx,offset invseq ; Put into reverse video. int dos mov ah,prstr mov dx,offset ivlseq ; Make line inverse video int dos puth1: lodsb cmp al,0 ; Terminated with a null. je puth2 mov dl,al mov ah,conout int dos cmp al,lf ; Line feed? je puth0 ; Yes, clear the next line. jmp puth1 ; Else, just keep on writing. puth2: mov dx,offset crlf mov ah,prstr int dos mov dx,offset nrmseq ; Normal video. int dos ret puthlp endp ; Perform a delete. DODEL PROC NEAR mov ah,prstr mov dx,offset delstr ; Erase weird character. int dos ret DODEL ENDP ; Perform a Control-U. CTLU PROC NEAR mov ah,prstr mov dx,offset clrlin int dos ret CTLU ENDP COMS PROC NEAR mov dx,offset comptab mov bx,offset comphlp mov ah,cmkey call comnd jmp r push bx mov ah,cmcfm call comnd ; Get a confirm. jmp comx ; Didn't get a confirm. nop pop bx mov flags.comflg,bl ; Set the comm port flag. cmp flags.comflg,1 ; Using Com 1? jne coms0 ; Nope. mov ax,offset port1 mov portval,ax ret coms0: mov ax,offset port2 mov portval,ax ret comx: pop bx ret COMS ENDP VTS PROC NEAR jmp notimp VTS ENDP notimp: mov ah,prstr mov dx,offset noimp int dos jmp prserr lclini: ret showkey: mov ax,offset shkmsg mov cx,shklen ret ; Common initialization for using serial port. SERINI PROC NEAR call opnprt call clrbuf ; Clear input buffer. [20e] ret ; We're done. [21c] SERINI ENDP SERRST PROC NEAR mov bx,prthnd cmp bx,0 ; none there? je serrs1 ; no, don't try to close. mov ah,close int dos ; close handle mov prthnd,0 serrs1: ret ; All done. [21c] SERRST ENDP ; Generate a short beep. BEEP PROC NEAR mov dl,bell mov ah,conout int dos ret BEEP 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 term proc near mov si,ax ; this is source mov di,offset ourarg ; place to store arguments mov ax,ds mov es,ax ; address destination segment mov cx,size termarg rep movsb ; copy into our arg blk term1: call prtchr jmp short term2 ; have a char... nop nop jmp short term3 ; no char, go on term2: push ax mov dl,al and dl,7fh ; mask off parity for terminal mov ah,dconio int dos ; write out the character pop ax test ourarg.flgs,capt ; capturing output? jz term3 ; no, forget it call ourarg.captr ; else call the routine term3: mov ah,dconio mov dl,0ffh int dos jz term1 ; no character, go on cmp al,ourarg.escc ; escape char? je term4 ; yes, exit push ax ; save char mov ah,al or ah,80H ; turn on hi bit so DOS doesn't interfere call outchr ; output the character nop nop nop pop ax test ourarg.flgs,lclecho ; echoing? jz term1 ; no, continue loop mov dl,al mov ah,dconio int dos jmp term1 ; else echo and keep going term4: ret term endp code ends end //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 640 msxhp150.asm /bin/echo -n ' '; /bin/ls -ld msxhp150.asm fi /bin/echo 'Extracting msxibm.asm' sed 's/^X//' <<'//go.sysin dd *' >msxibm.asm ; Kermit system dependent module for IBM-PC public serini, serrst, clrbuf, outchr, coms, vts, dodel, public ctlu, cmblnk, locate, prtchr, dobaud, clearl, public dodisk, getbaud, beep, public count, xofsnt, puthlp, putmod, clrmod, poscur public sendbr, machnam, setktab, setkhlp, lclini, showkey include msdefs.h false equ 0 true equ 1 mntrgh equ bufsiz*3/4 ; High point = 3/4 of buffer full. ; constants used by serial port handler BRKBIT EQU 040H ; Send-break bit. TIMER EQU 40H ; Use to issue short beep. PORT_B EQU 61H ; Port B address. MCONF EQU 11H ; Machine configuration. KEYB EQU 16H BIOS EQU 10H MDMDAT1 EQU 03F8H ; Address of modem port (data). [19b] MDMSTS1 EQU 03FDH ; Address of modem port status. [19b] MDMCOM1 EQU 03FBH ; Address of modem port command. [19b] MDMDAT2 EQU 02F8H ; Port 2 address. [19b] MDMSTS2 EQU 02FDH ; Port 2 status. [19b] MDMCOM2 EQU 02FBH ; Port 2 command. [19b] MDMINP EQU 1 ; Input ready bit. MDMINTV EQU 0030H ; Address of modem port interrupt vector. MDINTV2 EQU 002CH ; Address for port 2. [19b] MDMINTO EQU 0EFH ; Mask to enable interrupt for modem port. MDINTO2 EQU 0F7H ; Enable interrupt level 3. [19b] MDMINTC EQU 010H ; Bit to set to disable interrupts for modem. MDINTC2 EQU 008H ; Disable IRQ3. [19b] INTCONT EQU 0021H ; Address of 8259 interrupt controller ICW2-3. INTCON1 EQU 0020H ; Address of 8259 ICW1. EOICOM EQU 0064H ; End of interrupt. EOICOM2 EQU 0063H ; End of interrupt for COM2. [19b] ; external variables used: ; drives - # of disk drives on system ; flags - global flags as per flginfo structure defined in pcdefs ; trans - global transmission parameters, trinfo struct defined in pcdefs ; portval - pointer to current portinfo structure (currently either port1 ; or port2) ; port1, port2 - portinfo structures for the corresponding ports ; global variables defined in this module: ; xofsnt, xofrcv - tell whether we saw or sent an xoff. ; setktab - keyword table for redefining keys (should contain a 0 if ; not implemented) ; setkhlp - help for setktab. datas segment public 'datas' extrn drives:byte,flags:byte, trans:byte extrn portval:word, port1:byte, port2:byte setktab db 12 mkeyw 'BACKSPACE',0eh mkeyw 'F1',3bh mkeyw 'F2',3ch mkeyw 'F3',3dh mkeyw 'F4',3eh mkeyw 'F5',3fh mkeyw 'F6',40h mkeyw 'F7',41h mkeyw 'F8',42h mkeyw 'F9',43h mkeyw 'F10',44h mkeyw 'SCAN',-1 setkhlp db cr,lf,'Keyname: backspace, f1, ... f10, or "SCAN" follwed by ' db 'decimal scan code$' brkval db 0 ; What to send for a break. brkadr dw 0 ; Where to send it. modem mdminfo <MDMDAT1,MDMSTS1,MDMCOM1,MDMINTO,MDMINTC,EOICOM,MDMINTV> erms20 db cr,lf,'?Warning: System has no disk drives$' ; [21a] erms40 db cr,lf,'?Warning: Unrecognized baud rate$' badbd db cr,lf,'Unimplemented baud rate$' machnam db 'IBM-PC$' crlf db cr,lf,'$' delstr db BS,' ',BS,'$' ; Delete string. [21d] clrlin db cr,'$' ; Clear line (just the cr part). savsci dw ? ; Save for serial port interrupt vector. [14] savscs dw ? ; Ditto. [14] savbr1 dw ? ; "Break" interrupt vector. [25] savbr2 dw ? ; Ditto. [25] portin db 0 ; Has comm port been initialized. [21c] xofsnt db 0 ; Say if we sent an XOFF. xofrcv db 0 ; Say if we received an XOFF. tmp db ?,'$' temp dw 0 temp1 dw ? ; Temporary storage. temp2 dw ? ; Temporary storage. ontab db 02H ; Two entries. db 03H,'OFF$' ; Should be alphabetized. [19a] dw 00H db 02H,'ON$' dw 01H comptab db 04H db 01H,'1$' dw 01H db 01H,'2$' dw 00H db 04H,'COM1$' dw 01H db 04H,'COM2$' dw 00H ; this table is indexed by the baud rate definitions given in ; pcdefs. Unsupported baud rates should contain FF. bddat label word dw 0FFH ; 45.5 baud -- Not supported. dw 900H ; 50 baud dw 600H ; 75 baud dw 417H ; 110 baud dw 359H ; 134.5 baud dw 300H ; 150 baud dw 180H ; 300 baud dw 0C0H ; 600 baud dw 60H ; 1200 baud dw 40H ; 1800 baud dw 3AH ; 2000 baud dw 30H ; 2400 baud dw 18H ; 4800 baud dw 0CH ; 9600 baud dw 0FFH ; 19200 baud -- Not supported. dw 0FFH ; 38400 baud -- Not supported. ; variables for serial interrupt handler source db bufsiz DUP(?) ; Buffer for data from port. srcpnt dw 0 ; Pointer in buffer (DI). count dw 0 ; Number of chars in int buffer. savesi dw 0 ; Save SI register here. telflg db 0 ; Are we acting as a terminal. mst dw 0 ; Modem status address. mdat dw 0 ; Modem data address. mdeoi db 0 ; End-of-Interrupt value. rbtrn db 7fH ; rubout shkbuf db 300 dup (?) ; room for definition shkmsg db ' Scan code: ' shkmln equ $-shkmsg shkms1 db cr,lf,' Definition: ' shkm1ln equ $-shkms1 datas ends code segment public extrn comnd:near, dopar:near, defkey:near, gss:near assume cs:code,ds:datas ; local initialization lclini proc near mov ax,0eH ; scan code for arrow key mov si,offset rbtrn ; translate to rubout mov cx,1 ; one char translation call defkey mov brkval,BRKBIT ; What to send for a break. mov ax,modem.mdcom ; Where to send it. mov brkadr,ax ret lclini endp ; this is called by Kermit initialization. It checks the ; number of disks on the system, sets the drives variable ; appropriately. Returns normally. DODISK PROC NEAR int mconf ; Get equipment configuration. mov ah,al ; Store AL value for a bit. and al,01H ; First, look at bit 0. jz dodsk0 ; No disk drives -- forget it. mov al,ah ; Get back original value. mov cl,6 ; Shift over bits 6 and 7. shr al,cl ; To positions 0 and 1. inc al ; Want 1 thru 4 (not 0 thru 3). mov drives,al ; Remember how many. ret dodsk0: mov ah,prstr ; Print a warning message. mov dx,offset erms20 ; I'm not sure if things will int dos ; work with only a cassette. mov drives,0 ; Say there aren't any drives. ret DODISK ENDP ; show the definition of a key. The terminal argument block (which contains ; the address and length of the definition tables) is passed in ax. ; Returns a string to print in AX, length of same in CX. ; Returns normally. showkey proc near push es push ax ; save the ptr mov bx,ds mov es,bx ; address data segment cld showk1: xor ah,ah int keyb ; read a char push ax ; save the character call gss ; get shift state pop bx mov ah,al ; shift state to ah mov al,bh ; scan code to al push ax ; remember scan code mov di,offset shkbuf mov si,offset shkmsg mov cx,shkmln rep movsb ; copy in initial message call nout ; write out scan code mov si,offset shkms1 mov cx,shkm1ln ; second message rep movsb pop ax ; get scan code back pop bx ; and terminal arg block mov cx,[bx].klen ; and length jcxz showk2 ; no table, not defined push di ; remember output ptr mov di,[bx].ktab ; get key table repne scasw ; search for a definition for this mov si,di ; remember result ptr pop di ; get output ptr back jne showk2 ; not defined, forget it sub si,[bx].ktab ; compute offset from beginning sub si,2 ; minus 2 for pre-increment add si,[bx].krpl ; get index into replacement table mov si,[si] ; pick up replacement mov cl,[si] ; get length mov ch,0 inc si rep movsb ; copy into buffer showk2: mov ax,offset shkbuf ; this is buffer mov cx,di sub cx,ax ; length pop es ret ; and return showkey endp ; Clear the input buffer. This throws away all the characters in the ; serial interrupt buffer. This is particularly important when ; talking to servers, since NAKs can accumulate in the buffer. ; Returns normally. CLRBUF PROC NEAR cli mov ax,offset source mov srcpnt,ax mov savesi,ax mov count,0 sti ret CLRBUF ENDP ; Clear to the end of the current line. Returns normally. CLEARL PROC NEAR mov ah,3 ; Clear to end of line. mov bh,0 int bios ; Get current cursor position mov cx,dx mov dl,79 mov ah,7 mov al,0 mov bh,7 int bios ret CLEARL ENDP ; Put the char in AH to the serial port. This assumes the ; port has been initialized. Should honor xon/xoff. Skip returns on ; success, returns normally if the character cannot be written. outchr: mov bp,portval cmp ds:[bp].floflg,0 ; Are we doing flow control. je outch2 ; No, just continue. xor cx,cx ; clear counter outch1: cmp xofrcv,true ; Are we being held? jne outch2 ; No - it's OK to go on. loop outch1 ; held, try for a while mov xofrcv,false ; timed out, force it off and fall thru. outch2: push dx ; Save register. sub cx,cx mov al,ah ; Parity routine works on AL. call dopar ; Set parity appropriately. mov ah,al ; Don't overwrite character with status. mov dx,modem.mdstat ; Get port status. outch3: in al,dx test al,20H ; Transmitter ready? jnz outch4 ; Yes loop outch3 jmp outch5 ; Timeout outch4: mov al,ah ; Now send it out mov dx,modem.mddat out dx,al pop dx jmp rskp outch5: pop dx ret ; This routine blanks the screen. Returns normally. CMBLNK PROC NEAR ; This is stolen from the IBM example. mov cx,0 mov dx,184FH mov bh,7 mov ax,600H int bios ret CMBLNK ENDP ; Locate: homes the cursor. Returns normally. LOCATE PROC NEAR mov dx,0 ; Go to top left corner of screen. jmp poscur LOCATE ENDP ; write a line in inverse video at the bottom of the screen... ; the line is passed in dx, terminated by a $. Returns normally. putmod proc near push dx ; preserve message mov cx,1800h mov dx,184fh mov ax,600h ; scroll to clear the line mov bh,70h ; inverse video int bios mov dx,1800h ; now address line 24 call poscur pop dx ; get message back mov ah,prstr int dos ; write it out ret ; and return putmod endp ; clear the mode line written by putmod. Returns normally. clrmod proc near mov cx,1800h mov dx,184fh mov ax,600h mov bh,7h int bios ret clrmod endp ; put a help message on the screen. This one uses reverse video... ; pass the message in ax, terminated by a null. Returns normally. puthlp proc near push ax ; preserve this mov si,ax ; point to it mov dh,1 ; init counter puthl1: lodsb ; get a byte cmp al,lf ; linefeed? jne puthl2 ; no, keep going inc dh ; count it jmp puthl1 ; and keep looping puthl2: cmp al,0 ; end of string? jne puthl1 ; no, keep going mov ax,600h ; scroll to clear window xor cx,cx ; from top left mov dl,4fh ; to bottom right of needed piece mov bh,70h ; inverse video int bios call locate ; home cursor pop si ; point to string again puthl3: lodsb ; get a byte cmp al,0 ; end of string? je puthl4 ; yes, stop mov ah,14 int bios ; else write to screen jmp puthl3 ; and keep going puthl4: mov dx,24 * 100H ; go to last line jmp poscur ; position and return puthlp endp ; Set the baud rate for the current port, based on the value ; in the portinfo structure. Returns normally. DOBAUD PROC NEAR mov bp,portval mov temp1,ax ; Don't overwrite previous rate. [25] mov ax,ds:[bp].baud ; Check if new rate is valid. [25] mov tmp,2 mul tmp ; Get index into baud table. mov bx,offset bddat ; Start of table. add bx,ax mov ax,[bx] ; The data to output to port. cmp ax,0FFH ; Unimplemented baud rate. jne dobd0 mov ax,temp1 ; Get back orginal value. mov ds:[bp].baud,ax ; Leave baud rate as is. mov ah,prstr mov dx,offset badbd ; Give an error message. int dos ret dobd0: mov temp1,ax ; Remember value to output. [25] mov dx,modem.mdcom ; LCR -- Initialize baud rate. [19b] in al,dx mov bl,al or ax,80H out dx,al mov dx,modem.mddat ; [19b] mov ax,temp1 out dx,al inc dx mov al,ah out dx,al mov dx,modem.mdcom ; [19b] mov al,bl out dx,al ret DOBAUD ENDP ; Get the current baud rate from the serial card and set it ; in the portinfo structure for the current port. Returns normally. ; This is used during initialization. GETBAUD PROC NEAR mov dx,modem.mdcom ; Get current Line Control Register value. in al,dx mov bl,al ; Save it. or ax,80H ; Turn on to access baud rate generator. out dx,al mov dx,modem.mddat ; Divisor latch. inc dx in al,dx ; Get hi order byte. mov ah,al ; Save here. dec dx in al,dx ; Get lo order byte. push ax mov dx,modem.mdcom mov al,bl ; Restore old value. out dx,al pop ax cmp ax,0FFFFH ; Who knows what this is. je getb2 mov bx,offset bddat ; Find rate's offset into table. mov cl,0 ; Keep track of index. getb0: cmp ax,[bx] je getb1 inc cl cmp cl,baudsiz ; At the end of the list. jge getb2 add bx,2 jmp getb0 getb1: mov ch,0 mov bp,portval mov ds:[bp].baud,cx ; Set baud rate. ret getb2: mov ah,prstr mov dx,offset erms40 int dos ret GETBAUD ENDP ; skip returns if no character available at port, ; otherwise returns with char in al, # of chars in buffer in dx. PRTCHR PROC NEAR call chkxon ; see if we need to xon cmp count,0 jnz prtch2 jmp rskp ; No data - check console. prtch2: mov si,savesi lodsb ; get a byte cmp si,offset source + bufsiz ; bigger than buffer? jb prtch1 ; no, keep going mov si,offset source ; yes, wrap around prtch1: dec count mov savesi,si mov dx,count ; return # of chars in buffer ret PRTCHR ENDP ; local routine to see if we have to transmit an xon chkxon proc near push bx mov bx,portval cmp [bx].floflg,0 ; doing flow control? je chkxo1 ; no, skip all this cmp xofsnt,false ; have we sent an xoff? je chkxo1 ; no, forget it cmp count,mntrgh ; below trigger? jae chkxo1 ; no, forget it mov ax,[bx].flowc ; ah gets xon call outchr ; send it nop nop nop ; in case it skips mov xofsnt,false ; remember we've sent the xon. chkxo1: pop bx ; restore register ret ; and return chkxon endp ; Send a break out the current serial port. Returns normally. SENDBR PROC NEAR push cx push dx push ax xor cx,cx ; Clear loop counter. mov dx,brkadr ; Port address. [19b] in al,dx ; Get current setting. or al,brkval ; Set send-break bit(s). out dx,al ; Start the break. pause: loop pause ; Wait a while. xor al,brkval ; Clear send-break bit(s). out dx,al ; Stop the break. pop ax pop dx pop cx ret ; And return. SENDBR ENDP ; Position the cursor according to contents of DX: ; DH contains row, DL contains column. Returns normally. POSCUR PROC NEAR mov ah,2 ; Position cursor. mov bh,0 int bios ret POSCUR ENDP ; Delete a character from the terminal. This works by printing ; backspaces and spaces. Returns normally. DODEL PROC NEAR mov ah,prstr mov dx,offset delstr ; Erase weird character. int dos ret DODEL ENDP ; Move the cursor to the left margin, then clear to end of line. ; Returns normally. CTLU PROC NEAR mov ah,prstr mov dx,offset clrlin int dos call clearl ret CTLU ENDP ; set the current port. COMS PROC NEAR mov dx,offset comptab mov bx,0 mov ah,cmkey call comnd jmp r push bx mov ah,cmcfm call comnd ; Get a confirm. jmp comx ; Didn't get a confirm. nop pop bx mov flags.comflg,bl ; Set the comm port flag. cmp flags.comflg,1 ; Using Com 1? jne coms0 ; Nope. mov ax,offset port1 mov portval,ax mov modem.mddat,MDMDAT1 ; Set COM1 defaults. mov modem.mdstat,MDMSTS1 mov modem.mdcom,MDMCOM1 mov modem.mddis,MDMINTC mov modem.mden,MDMINTO mov modem.mdmeoi,EOICOM mov modem.mdintv,MDMINTV mov brkadr,MDMCOM1 ret coms0: mov ax,offset port2 mov portval,ax mov modem.mddat,MDMDAT2 ; Set COM2 defaults. mov modem.mdstat,MDMSTS2 mov modem.mdcom,MDMCOM2 mov modem.mddis,MDINTC2 mov modem.mden,MDINTO2 mov modem.mdmeoi,EOICOM2 mov modem.mdintv,MDINTV2 mov brkadr,MDMCOM2 ret comx: pop bx ret COMS ENDP ; Set heath emulation on/off. VTS PROC NEAR mov dx,offset ontab mov bx,0 mov ah,cmkey call comnd jmp r push bx mov ah,cmcfm call comnd ; Get a confirm. jmp vt0 ; Didn't get a confirm. nop pop bx mov flags.vtflg,bl ; Set the VT52 emulation flag. ret vt0: pop bx ret VTS ENDP ; initialization for using serial port. This routine performs ; any initialization necessary for using the serial port, including ; setting up interrupt routines, setting buffer pointers, etc. ; Doing this twice in a row should be harmless (this version checks ; a flag and returns if initialization has already been done). ; SERRST below should restore any interrupt vectors that this changes. ; Returns normally. SERINI PROC NEAR push es cmp portin,0 ; Did we initialize port already? [21c] jne serin0 ; Yes, so just leave. [21c] cli ; Disable interrupts cld ; Do increments in string operations xor ax,ax ; Address low memory mov es,ax mov bx,modem.mdintv ; Save serial card interrupt vector. [19b] mov ax,es:[bx] mov savsci,ax mov ax,offset serint ; And point it to my routine mov es:[bx],ax add bx,2 ; Save CS register too. [19b] mov ax,es:[bx] mov savscs,ax mov es:[bx],cs mov portin,1 ; Remember port has been initialize. call clrbuf ; Clear input buffer. mov ax,modem.mdstat mov mst,ax ; Use this address for status. mov ax,modem.mddat mov mdat,ax ; Use this address for data. mov al,modem.mdmeoi mov mdeoi,al ; Use to signify end-of-interrupt. in al,21H ; Set up 8259 interrupt controller and al,modem.mden ; Enable INT3 or INT4. out 21H,al mov dx,modem.mdcom ; Set up the serial card. mov al,3 out dx,al mov dl,0F9H mov al,1 ; Set up interrupt enable register out dx,al mov dl,0FCH ; Enable interrupts from serial card mov al,0BH out dx,al sti ; Allow interrupts mov dl,0F8H in al,dx serin0: pop es ret ; We're done. SERINI ENDP ; Reset the serial port. This is the opposite of serini. Calling ; this twice without intervening calls to serini should be harmless. ; Returns normally. SERRST PROC NEAR push es ; preserve this cmp portin,0 ; Reset already? je srst1 ; Yes, just leave. cli ; Disable interrupts mov dx,03FCH ; Disable modem interrupts cmp flags.comflg,1 ; Using port 1 ? je srst0 ; Yes - continue. mov dh,02 ; Set for port 2. srst0: mov al,3 out dx,al in al,21H ; Interrupt controller or al,modem.mddis ; Inhibit IRQ3 or IRQ4. out 21H,al xor bx,bx ; Address low memory mov es,bx mov bx,modem.mdintv ; Restore the serial card int vector mov ax,savsci mov es:[bx],ax add bx,2 ; Restore CS too. mov ax,savscs mov es:[bx],ax mov portin,0 ; Reset flag. sti srst1: pop es ret ; All done. SERRST ENDP ; serial port interrupt routine. This is not accessible outside this ; module, handles serial port receiver interrupts. SERINT PROC NEAR push bx push dx push ax push es push di push ds push bp push cx cld mov ax,seg datas mov ds,ax ; address data segment mov es,ax mov di,srcpnt ; Registers for storing data. mov dx,mst ; Asynch status port. [19b] in al,dx test al,mdminp ; Data available? jz retint ; Nope. mov dx,mdat ; [19b] in al,dx cmp telflg,0 ; File transfer or terminal mode? [17c] jz srint0 and al,7FH ; Terminal mode (7 bits only). srint0: or al,al jz retint ; Ignore nulls. mov ah,al and ah,7fH ; strip parity temporarily cmp ah,7FH ; Ignore rubouts, too. jz retint mov bp,portval cmp ds:[bp].floflg,0 ; Doing flow control? je srint2 ; Nope. mov bx,ds:[bp].flowc ; Flow control char (BH = XON, BL = XOFF). cmp al,bl ; Is it an XOFF? jne srint1 ; Nope, go on. mov xofrcv,true ; Set the flag. jmp retint srint1: cmp al,bh ; Get an XON? jne srint2 ; No, go on. mov xofrcv,false ; Clear our flag. jmp retint srint2: stosb cmp di,offset source + bufsiz jb srint3 ; not past end... mov di,offset source ; wrap buffer around srint3: inc count cmp ds:[bp].floflg,0 ; Doing flow control? je retint ; No, just leave. cmp xofsnt,true ; Have we sent an XOFF? je retint ; Yes. cmp count,mntrgh ; Past the high trigger point? jbe retint ; No, we're within our limit. mov ah,bl ; Get the XOFF. call outchr ; Send it. nop nop nop ; ignore failure. mov xofsnt,true ; Remember we sent it. retint: mov srcpnt,di sti mov al,mdeoi ; [19b] out intcon1,al ; Send End-of-Interrupt to 8259. pop cx pop bp pop ds pop di pop es pop ax pop dx pop bx intret: iret SERINT ENDP ; Produce a short beep. The PC DOS bell is long enough to cause a loss ; of data at the port. Returns normally. BEEP PROC NEAR mov al,10110110B ; Gen a short beep (long one losses data.) out timer+3,al ; Code snarfed from Technical Reference. mov ax,533H out timer+2,al mov al,ah out timer+2,al in al,port_b mov ah,al or al,03 out port_b,al sub cx,cx mov bl,1 beep0: loop beep0 dec bl jnz beep0 mov al,ah out port_b,al ret BEEP ENDP ; put the number in ax into the buffer pointed to by di. Di is updated nout proc near mov dx,0 ; high order is always 0. mov bx,10 div bx ; divide to get digit push dx ; save remainder digit or ax,ax ; test quotient jz nout1 ; zero, no more of number call nout ; else call for rest of number nout1: pop ax ; get digit back add al,'0' ; make printable stosb ; drop it off ret ; and return nout 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 //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 640 msxibm.asm /bin/echo -n ' '; /bin/ls -ld msxibm.asm fi /bin/echo 'Extracting msxrb.asm' sed 's/^X//' <<'//go.sysin dd *' >msxrb.asm ; Kermit system dependent module for Rainbow ; Jeff Damens, July 1984 public serini, serrst, clrbuf, outchr, coms, vts, dodel, public ctlu, cmblnk, locate, lclini, prtchr, dobaud, clearl, public dodisk, getbaud, beep, public count, xofsnt, puthlp, putmod, clrmod, poscur public sendbr, term, machnam, setktab, setkhlp, showkey include msdefs.h ; rainbow-dependent screen constants scrseg equ 0ee00H ; screen segment latofs equ 0ef4h ; ptrs to line beginnings, used by firmware l1ptr equ latofs ; ptr to first line llptr equ latofs+23*2 ; ptr to last line csrlin equ 0f42h ; current cursor line. ; level 1 console definitions fnkey equ 100H ; function key flag shfkey equ 200H ; shift key ctlkey equ 400H ; control key cplk equ 800H prvkey equ 23H nxtkey equ 25H brkkey equ 65H prtkey equ 3 false equ 0 true equ 1 mntrgh equ bufsiz*3/4 ; High point = 3/4 of buffer full. mnstata equ 042H ;Status/command port A mnstatb equ 043H ;Status/command port B. mndata equ 040H ;Data port. mndatb equ 041H mnctrl equ 002H ;Control port. serchn equ 0A4H ; interrupt to use serch1 equ 044H ; use this too for older rainbows. txrdy EQU 04H ;Bit for output ready. rxrdy EQU 01H ;Bit for input ready. fastcon equ 29H ; fast console handler firmwr equ 18H swidth equ 132 ; screen width slen equ 24 ; screen length npgs equ 5 ; # of pages to remember stbrk equ 15 ; start sending a break enbrk equ 16 ; stop sending break. ; external variables used: ; drives - # of disk drives on system ; flags - global flags as per flginfo structure defined in pcdefs ; trans - global transmission parameters, trinfo struct defined in pcdefs ; portval - pointer to current portinfo structure (currently either port1 ; or port2) ; port1, port2 - portinfo structures for the corresponding ports ; global variables defined in this module: ; xofsnt, xofrcv - tell whether we saw or sent an xoff. ; circular buffer ptr cbuf struc pp dw ? ; place ptr in buffer bend dw ? ; end of buffer orig dw ? ; buffer origin lcnt dw 0 ; # of lines in buffer. cbuf ends ; answerback structure ans struc anspt dw ? ; current pointer in answerback ansct db ? ; count of chars in answerback ansseq dw ? ; pointer to whole answerback anslen db ? ; original length ansrtn dw ? ; routine to call. ans ends datas segment public 'datas' extrn drives:byte,flags:byte, trans:byte extrn portval:word, port1:byte, port2:byte setktab db 22 mkeyw 'F4',fnkey+5h mkeyw 'F5',fnkey+65h mkeyw 'F6',fnkey+7h mkeyw 'F7',fnkey+9h mkeyw 'F8',fnkey+0Bh mkeyw 'F9',fnkey+0Dh mkeyw 'F10',fnkey+0Fh mkeyw 'F11',esc mkeyw 'F12',bs mkeyw 'F13',lf mkeyw 'F14',fnkey+11h mkeyw 'F17',fnkey+13h mkeyw 'F18',fnkey+15h mkeyw 'F19',fnkey+17h mkeyw 'F20',fnkey+19h mkeyw 'FIND',fnkey+1bh mkeyw 'INSERTHERE',fnkey+1dh mkeyw 'REMOVE',fnkey+1fh mkeyw 'SCAN',-1 mkeyw 'SELECT',fnkey+21h ourflgs db 0 ; our flags fpscr equ 80H ; flag definitions... crlf db cr,lf setkhlp db ' F4 ... F20 or SCAN$' machnam db 'Rainbow$' nyimsg db cr,lf,'Not yet implemented$' delstr db BS,' ',BS,'$' ; Delete string. clrlin db cr,'$' ; Clear line (just the cr part). oldser dw ? ; old serial handler oldseg dw ? ; segment of above old1ser dw ? ; old serial handler, alternate address old1seg dw ? ; segment of same. portin db 0 ; Has comm port been initialized. xofsnt db 0 ; Say if we sent an XOFF. xofrcv db 0 ; Say if we received an XOFF. iobuf db 5 dup (?) ; buffer for ioctl phbuf db swidth dup (?) gopos db esc,'[' rowp db 20 dup (?) clrseq db esc,'[H',esc,'[J$' ceolseq db esc,'[K$' invseq db esc,'[7m$' nrmseq db esc,'[0m$' ivlatt db swidth dup (0fH) ; a line's worth of inverse attribute ; special keys. spckey dw prvkey,nxtkey,brkkey,prtkey,prtkey+ctlkey,prvkey+ctlkey dw nxtkey+ctlkey,brkkey+ctlkey spclen equ ($-spckey)/2 ; special key handlers. Must parallel spckey spchnd dw prvscr,nxtscr,sendbr,prtscr,togprt,prvlin,nxtlin,sendbr ; arrow and PF keys arrkey db 27H,29H,2bH,2dH,59H,5cH,5fH,62H arrlen equ $-arrkey ; translations for arrow and PF keys, must parallel arrkey arrtrn dw uptrn,dntrn,rgttrn,lfttrn dw pf1trn,pf2trn,pf3trn,pf4trn ; keypad keys keypad db 2fh,32h,35h,38h,3bh,3eh,41h,44h,47h,4ah,4dh,50h,53h,56h keypln equ $-keypad ; keytrn and altktrn must parallel keypad keytrn db '0123456789-,.',cr altktrn db 'pqrstuvwxymlnM' keyptr dw keytrn ; pointer to correct translation table akeyflg db 0 ; non-zero if in alt keypad mode. ; arrow and PF key translations uptrn db 3,esc,'[A' dntrn db 3,esc,'[B' rgttrn db 3,esc,'[C' lfttrn db 3,esc,'[D' pf1trn db 3,esc,'OP' pf2trn db 3,esc,'OQ' pf3trn db 3,esc,'OR' pf4trn db 3,esc,'OS' ourarg termarg <> ; variables for serial interrupt handler source db bufsiz DUP(?) ; Buffer for data from port. srcpnt dw 0 ; Pointer in buffer (DI). count dw 0 ; Number of chars in int buffer. savesi dw 0 ; Save SI register here. telflg db 0 ; non-zero if we're a terminal. NRU. respkt db 10 dup (?) ; ioctl packet ivec dw tranb ; transmit empty B dw tranb ; status change B dw tranb ; receive b dw tranb ; special receive b dw stxa ; transmit empty a dw sstata ; status change a dw srcva ; receive a dw srcva ; special receive a ; multi-screen stuff bsize equ swidth*slen*npgs ; # of bytes needed to store screens tbuf db bsize dup (?) bbuf db bsize dup (?) ; top and bottom buffers topbuf cbuf <tbuf,tbuf+bsize-1,tbuf,0> botbuf cbuf <bbuf,bbuf+bsize-1,bbuf,0> tlbuf db swidth dup (?) ; top line temp buffer blbuf db swidth dup (?) ; bottom line temp buffer rlbuf db swidth dup (?) ; line temp buffer prbuf db swidth dup (?) ; print temp buffer topdwn db esc,'[H',esc,'M$' ; go to top, scroll down botup db esc,'[24;0H',esc,'D$' ; go to bottom, scroll up curinq db esc,'[6n$' ; cursor inquiry posbuf db 20 dup (?) ; place to store cursor position gtobot db esc,'[24;0H$' ; go to bottom of screen. ourscr db slen*swidth dup (?) ourattr db slen*swidth dup (?) ; storage for screen and attributes inited db 0 ; terminal handler not inited yet. dosmsg db '?Must be run in version 2.05 or higher$' anssq1 db esc,'[c' ansln1 equ $-anssq1 anssq2 db esc,'Z' ansln2 equ $-anssq2 eakseq db esc,'=' dakseq db esc,'>' ansbk1 ans <anssq1,ansln1,anssq1,ansln1,sndans> ; two answerbacks ansbk2 ans <anssq2,ansln2,anssq2,ansln2,sndans> ansbk3 ans <eakseq,2,eakseq,2,enaaky> ; enable alt keypad ansbk4 ans <dakseq,2,dakseq,2,deaaky> ; disable alt keypad ansret db esc,'[?6c' ansrln equ $-ansret shkbuf db 300 dup (?) ; room for definition shkmsg db ' Scan code: ' shkmln equ $-shkmsg shkms1 db cr,lf,' Definition: ' shkm1ln equ $-shkms1 datas ends code segment public extrn comnd:near, dopar:near assume cs:code,ds:datas ; local initialization routine, called by Kermit initialization. lclini proc near ; make sure this is DOS version 2.05 or higher... mov ah,dosver int dos xchg al,ah ; put major version in ah, minor in al cmp ax,205H ; is it 2.05? jae lclin1 ; yes, go on mov dx,offset dosmsg call tmsg mov ax,4c00H ; exit(0) int dos lclin1: mov flags.vtflg,0 ; turn off heath emulation ret lclini endp ; this is called by Kermit initialization. It checks the ; number of disks on the system, sets the drives variable ; appropriately. The only problem is that a value of two ; is returned for single drive systems to be consistent ; with the idea of the system having logical drives A and ; B. Returns normally. DODISK PROC NEAR mov ah,gcurdsk ; Current disk value to AL. int dos mov dl,al ; Put current disk in DL. mov ah,seldsk ; Select current disk. int dos ; Get number of drives in AL. mov drives,al ret DODISK ENDP ; show the definition of a key. The terminal argument block (which contains ; the address and length of the definition tables) is passed in ax. ; Returns a string to print in AX, length of same in CX. ; Returns normally. showkey proc near push es push ax ; save the ptr cld showk1: mov di,6 ; get level one char int firmwr cmp cl,0ffH jne showk1 ; wait until char available mov bx,ds mov es,bx ; address data segment and ax,not cplk ; no caps lock push ax ; remember scan code mov di,offset shkbuf mov si,offset shkmsg mov cx,shkmln rep movsb ; copy in initial message call nout ; write out scan code mov si,offset shkms1 mov cx,shkm1ln ; second message rep movsb pop ax ; get scan code back pop bx ; and terminal arg block mov cx,[bx].klen ; and length jcxz showk2 ; no table, not defined push di ; remember output ptr mov di,[bx].ktab ; get key table repne scasw ; search for a definition for this mov si,di ; remember result ptr pop di ; get output ptr back jne showk2 ; not defined, forget it sub si,[bx].ktab ; compute offset from beginning sub si,2 ; minus 2 for pre-increment add si,[bx].krpl ; get index into replacement table mov si,[si] ; pick up replacement mov cl,[si] ; get length mov ch,0 inc si rep movsb ; copy into buffer showk2: mov ax,offset shkbuf ; this is buffer mov cx,di sub cx,ax ; length pop es ret ; and return showkey endp ; Clear the input buffer. This throws away all the characters in the ; serial interrupt buffer. This is particularly important when ; talking to servers, since NAKs can accumulate in the buffer. ; Returns normally. CLRBUF PROC NEAR cli mov ax,offset source mov srcpnt,ax mov savesi,ax mov count,0 sti ret CLRBUF ENDP ; Clear to the end of the current line. Returns normally. CLEARL PROC NEAR mov dx,offset ceolseq ; clear sequence jmp tmsg CLEARL ENDP ; Put the char in AH to the serial port. This assumes the ; port has been initialized. Should honor xon/xoff. Skip returns on ; success, returns normally if the character cannot be written. outchr: mov bp,portval cmp ds:[bp].floflg,0 ; Are we doing flow control. je outch2 ; No, just continue. xor cx,cx ; clear counter outch1: cmp xofrcv,true ; Are we being held? jne outch2 ; No - it's OK to go on. loop outch1 ; held, try for a while mov xofrcv,false ; timed out, force it off and fall thru. outch2: push dx ; Save register. sub cx,cx mov al,ah ; Parity routine works on AL. call dopar ; Set parity appropriately. mov ah,al ; Don't overwrite character with status. mov dx,mnstata ; port status register outch3: in al,dx test al,txrdy ; Transmitter ready? jnz outch4 ; Yes loop outch3 jmp outch5 ; Timeout outch4: mov al,ah ; Now send it out mov dx,mndata out dx,al pop dx jmp rskp outch5: pop dx ret ; This routine blanks the screen. Returns normally. CMBLNK PROC NEAR mov dx,offset clrseq ; clear screen sequence jmp tmsg CMBLNK ENDP ; Locate; homes the cursor. Returns normally. LOCATE PROC NEAR mov dx,0 ; Go to top left corner of screen. jmp poscur LOCATE ENDP ; write a line in inverse video at the bottom of the screen... ; the line is passed in dx, terminated by a $. Returns normally. putmod proc near push dx ; preserve message mov dx,24 * 100H ; line 24 call poscur mov dx,offset invseq ; put into inverse video call tmsg pop dx call tmsg ; print the message mov dx,offset nrmseq ; normal videw call tmsg ret ; and return putmod endp ; clear the mode line written by putmod. Returns normally. clrmod proc near mov dx,24 * 100H call poscur call clearl ret clrmod endp ; Put a help message on the screen. This one uses reverse video... ; pass the message in ax, terminated by a null. Returns normally. puthlp proc near push ax mov dx,slen * 100H ; go to bottom line call poscur pop ax push es mov bx,ds mov es,bx ; address data segment mov si,ax ; convenient place for this mov bx,101H ; current line/position puthl1: mov di,offset phbuf ; this is destination xor cx,cx ; # of chars in the line puthl2: lodsb ; get a byte cmp al,cr ; carriage return? je puthl2 ; yes, ignore it cmp al,lf ; linefeed? je puthl3 ; yes, break the loop cmp al,0 je puthl3 ; ditto for null dec cx ; else count the character stosb ; deposit into the buffer jmp puthl2 ; and keep going puthl3: add cx,80 ; this is desired length of the whole mov al,' ' rep stosb ; fill the line push bx push si push es ; firmware likes to eat this one mov ax,0 ; send chars and attributes mov cx,80 ; this is # of chars to send mov dx,offset ivlatt ; this are attributes to send mov si,offset phbuf ; the actual message mov di,14H ; send direct to screen mov bp,ds ; need data segment as well int firmwr ; go send it pop es pop si pop bx ; restore everything inc bx ; next line cmp byte ptr [si-1],0 ; were we ended by a 0 last time? jne puthl1 ; no, keep looping pop es ; else restore this ret ; and return puthlp endp ; Set the baud rate for the current port, based on the value ; in the portinfo structure. Returns normally. ; no baud rate. DOBAUD PROC NEAR mov dx,offset nyimsg call tmsg mov bx,portval mov [bx].baud,-1 ; keep baud rate unknown. ret DOBAUD ENDP ; Get the current baud rate from the serial card and set it ; in the portinfo structure for the current port. Returns normally. ; This is used during initialization. GETBAUD PROC NEAR ret ; no baud rate for now. GETBAUD ENDP ; skip returns if no character available at port, ; otherwise returns with char in al, # of chars in buffer in dx. PRTCHR PROC NEAR call chkxon ; see if we have to xon the host. cmp count,0 jnz prtch2 jmp rskp ; No data - check console. prtch2: mov si,savesi lodsb ; get a byte cmp si,offset source + bufsiz ; bigger than buffer? jb prtch1 ; no, keep going mov si,offset source ; yes, wrap around prtch1: dec count mov savesi,si mov dx,count ; return # of chars in buffer ret PRTCHR ENDP ; local routine to see if we have to transmit an xon chkxon proc near push bx mov bx,portval cmp [bx].floflg,0 ; doing flow control? je chkxo1 ; no, skip all this cmp xofsnt,false ; have we sent an xoff? je chkxo1 ; no, forget it cmp count,mntrgh ; below trigger? jae chkxo1 ; no, forget it mov ax,[bx].flowc ; ah gets xon call outchr ; send it nop nop nop ; in case it skips mov xofsnt,false ; remember we've sent an xon. chkxo1: pop bx ; restore register ret ; and return chkxon endp ; Send a break out the current serial port. Returns normally. SENDBR PROC NEAR push bx push cx push dx push ax mov ah,ioctl mov al,3 ; write to control channel. mov bx,3 ; aux port handle mov dx,offset iobuf mov iobuf,stbrk ; start sending a break int dos xor cx,cx ; clear loop counter pause: loop pause ; Wait a while. mov ah,ioctl mov al,3 mov bx,3 mov dx,offset iobuf mov iobuf,enbrk ; stop sending the break int dos pop ax pop dx pop cx pop bx ret ; And return. SENDBR ENDP ; Position the cursor according to contents of DX: ; DH contains row, DL contains column. Returns normally. POSCUR PROC NEAR add dx,101H ; start at 1,1 push es push dx cld mov ax,ds mov es,ax ; address right segment mov di,offset rowp mov al,dh ; row comes first mov ah,0 call nout mov al,';' stosb ; separated by a semi pop dx mov al,dl mov ah,0 call nout mov al,'H' stosb ; end w/H mov byte ptr [di],'$' ; and dollar sign mov dx,offset gopos call tmsg pop es ret POSCUR ENDP ; Delete a character from the terminal. This works by printing ; backspaces and spaces. Returns normally. DODEL PROC NEAR mov dx,offset delstr ; Erase weird character. jmp tmsg DODEL ENDP ; Move the cursor to the left margin, then clear to end of line. ; Returns normally. CTLU PROC NEAR mov dx,offset clrlin ; this just goes to left margin... call tmsg jmp clearl ; now clear line CTLU ENDP ; set the current port. COMS PROC NEAR mov dx,offset nyimsg jmp tmsg COMS ENDP ; Set heath emulation on/off. VTS PROC NEAR mov dx,offset nyimsg jmp tmsg VTS ENDP ; initialization for using serial port. This routine performs ; any initialization necessary for using the serial port, including ; setting up interrupt routines, setting buffer pointers, etc. ; Doing this twice in a row should be harmless (this version checks ; a flag and returns if initialization has already been done). ; SERRST below should restore any interrupt vectors that this changes. ; Returns normally. SERINI PROC NEAR push es cmp portin,0 ; Did we initialize port already? [21c] jne serin0 ; Yes, so just leave. [21c] cli ; Disable interrupts cld ; Do increments in string operations xor ax,ax ; Address low memory mov es,ax mov ax,es:[4*serchn] ; get old serial handler mov oldser,ax ; save. mov ax,es:[4*serchn+2] ; get segment mov oldseg,ax ; save segment as well mov ax,es:[4*serch1] ; this is alternate for older rainbows mov old1ser,ax mov ax,es:[4*serch1+2] mov old1seg,ax ; pretty silly, huh? mov ax,offset serint ; point to our routine mov word ptr es:[4*serchn],ax ; point at our serial routine mov word ptr es:[4*serch1],ax ; have to set both of these mov es:[4*serchn+2],cs ; our segment mov es:[4*serch1+2],cs mov al,030h ;[DTR] enable RTS and DTR out mnctrl,al ;[DTR] mov portin,1 ; Remember port has been initialized. call clrbuf ; Clear input buffer. sti ; Allow interrupts serin0: pop es ret ; We're done. SERINI ENDP ; this is used to by serini prtset proc near lodsb ; get a byte or al,al jz prtse1 ; end of table, stop here out dx,al ; else send it out jmp prtset ; and keep looping prtse1: ret ; end of routine prtset endp ; Reset the serial port. This is the opposite of serini. Calling ; this twice without intervening calls to serini should be harmless. ; Returns normally. SERRST PROC NEAR push es ; preserve this cmp portin,0 ; Reset already? je srst1 ; Yes, just leave. cli ; Disable interrupts xor ax,ax mov es,ax ; address segment 0 mov ax,oldser mov es:[4*serchn],ax mov ax,oldseg mov es:[4*serchn+2],ax mov ax,old1ser mov es:[4*serch1],ax mov ax,old1seg mov es:[4*serch1+2],ax ; restore old handlers mov portin,0 ; Reset flag. srst1: pop es ret ; All done. SERRST ENDP ; serial port interrupt routine. This is not accessible outside this ; module, handles serial port receiver interrupts. serint PROC NEAR push bx push dx push ax push es push di push ds push bp push cx cld mov ax,seg datas mov ds,ax ; address data segment mov es,ax mov di,srcpnt ; Registers for storing data. mov dx,mnstatb ; Asynch status port. mov al,0 ; innocuous value out dx,al ; send out to get into a known state... mov al,2 ; now address register 2 out dx,al in al,dx ; read interrupt cause cmp al,7 ; in range? ja serin7 ; no, just dismiss (what about reset error?) mov bl,al shl bl,1 ; double for word index mov bh,0 call ivec[bx] ; call appropriate handler serin7: mov dx,mnstata ; reload port address mov al,38H out dx,al ; tell the port we finished with the interrupt pop cx pop bp pop ds pop di pop es pop ax pop dx pop bx intret: iret ; handler for serial receive, port A srcva: mov dx,mnstata mov al,0 out dx,al ; put into known state... in al,dx test al,rxrdy ; Data available? jnz srcva1 ; yes, go read it jmp srcva7 srcva1: mov al,30H ; reset any errors out dx,al mov dx,mndata in al,dx ; read the character cmp telflg,0 ; File transfer or terminal mode? jz srcva2 and al,7FH ; Terminal mode (7 bits only). srcva2: or al,al jz srcva7 ; Ignore nulls. cmp al,7FH ; Ignore rubouts, too. jz srcva7 mov ah,al and ah,7fH ; only consider low-order 7 bits for flow ctl. mov bp,portval cmp ds:[bp].floflg,0 ; Doing flow control? je srcva4 ; Nope. mov bx,ds:[bp].flowc ; Flow control char (BH = XON, BL = XOFF). cmp ah,bl ; Is it an XOFF? jne srcva3 ; Nope, go on. mov xofrcv,true ; Set the flag. jmp short srcva7 srcva3: cmp ah,bh ; Get an XON? jne srcva4 ; No, go on. mov xofrcv,false ; Clear our flag. jmp srcva7 srcva4: stosb cmp di,offset source + bufsiz jb srcva5 ; not past end... mov di,offset source ; wrap buffer around srcva5: mov srcpnt,di ; update ptr inc count cmp ds:[bp].floflg,0 ; Doing flow control? je srcva7 ; No, just leave. cmp xofsnt,true ; Have we sent an XOFF? je srcva7 ; Yes. cmp count,mntrgh ; Past the high trigger point? jbe srcva7 ; No, we're within our limit. mov ah,bl ; Get the XOFF. call outchr ; Send it. nop nop nop ; ignore failure. mov xofsnt,true ; Remember we sent it. srcva7: ret ; The interrupt is for the 'B' port - transfer control to ; the original handler and hope for the best. tranb: pushf ; put flags on stack to simulate interrupt call dword ptr [old1ser] ; call old handler ret ; and return stxa: mov dx,mnstata mov al,28H ; reset transmit interrupt out dx,al ret sstata: mov dx,mnstata mov al,10H ; reset status interrupt out dx,al ret SERINT ENDP ; Produce a short beep. The PC DOS bell is long enough to cause a loss ; of data at the port. Returns normally. BEEP PROC NEAR mov dl,bell mov ah,conout int dos ret BEEP ENDP ; put the number in ax into the buffer pointed to by di. Di is updated nout proc near mov dx,0 ; high order is always 0. mov bx,10 div bx ; divide to get digit push dx ; save remainder digit or ax,ax ; test quotient jz nout1 ; zero, no more of number call nout ; else call for rest of number nout1: pop ax ; get digit back add al,'0' ; make printable stosb ; drop it off ret ; and return nout endp term proc near mov si,ax ; this is source mov di,offset ourarg ; place to store arguments mov ax,ds mov es,ax ; address destination segment mov cx,size termarg cld rep movsb ; copy into our arg blk cmp inited,0 ; inited yet? jz term1 ; no, keep going test ourarg.flgs,scrsam ; do they want us to leave it alone? jnz term1 ; yes, skip redisplay. call rstscr ; restore screen term1: mov inited,1 ; remember inited term2: call prtchr jmp short term3 ; have a char... nop nop jmp short term6 ; no char, go on term3: and al,7fh ; turn off parity for terminal mov bx,offset ansbk1 ; check 1st answerback call ansbak ; check for answerback mov bx,offset ansbk2 ; maybe second answerback call ansbak mov bx,offset ansbk3 call ansbak mov bx,offset ansbk4 call ansbak cmp al,lf ; linefeed? jne term4 ; no, keep going call scrprep ; need to save top line mov al,lf ; get char back term4: push ax int fastcon ; go print it pop ax test ourarg.flgs,capt ; capturing output? jz term5 ; no, forget it push ax call ourarg.captr ; else call the routine pop ax term5: test ourflgs,fpscr ; print screen toggled on? jz term6 ; no, keep going mov dl,al mov ah,lstout ; printer output int dos term6: mov di,6 ; get level 1 character push es int firmwr pop es ; don't let firmware steal registers. cmp cl,0ffh ; character available? je term7 ; no, do something else cmp cl,1 ; maybe level 2 sequence around jne term2 ; no, forget it mov di,2 ; get level 2 character push es int firmwr pop es cmp cl,0ffh ; did we really get one? jne term2 ; no, something strange happening. jmp term6 ; else skip and keep trying. term7: test ax,fnkey ; function-type key? jnz term8 ; yes, can't be escape character cmp al,ourarg.escc ; escape char? je term9 ; yes, exit term8: call trnout ; perform necessary translations, output char jmp term2 ; and loop around term9: call savscr ; save screen ret ; and return term endp ; enter with current terminal character in al, answerback ptr in bx. ; calls answerback routine if necessary. ; This can be used to make the emulator recognize any sequence. ansbak proc near push ax ; preserve this mov si,[bx].anspt ; get current pointer cmp al,[si] ; is it correct? jne ansba1 ; no, reset pointers and go on inc [bx].anspt ; increment pointer dec [bx].ansct ; decrement counter jnz ansba2 ; not done, go on push bx call [bx].ansrtn ; send answerback pop bx ansba1: mov ax,[bx].ansseq ; get original sequence mov [bx].anspt,ax mov al,[bx].anslen ; and length mov [bx].ansct,al ansba2: pop ax ret ansbak endp ; send the answerback message. sndans proc near mov si,offset ansret ; this is what we say mov cx,ansrln ; length of same sndan1: lodsb ; get a byte mov ah,al push si push cx call outchr nop nop nop pop cx pop si loop sndan1 ret sndans endp ; enable alternate keypad mode enaaky proc near mov akeyflg,1 ; remember alternate mode mov keyptr,offset altktrn ; set correct translate table ret enaaky endp ; disable alternate keypad mode deaaky proc near mov akeyflg,0 mov keyptr,offset keytrn ret deaaky endp ; enter with char and flags in ax. Does any necessary character translations, ; then outputs character trnout proc near and ax,not cplk ; forget about caps lock key test ourarg.flgs,havtt ; any translate table? jz trnou2 ; no, just output normally mov cx,ourarg.klen mov di,ourarg.ktab ; get redefined keys repne scasw ; look for this one jne trnou2 ; not found, try something else sub di,ourarg.ktab sub di,2 ; get index add di,ourarg.krpl ; get translation address mov si,[di] ; this is translation mov cl,[si] inc si ; pick up length, increment past it mov ch,0 jcxz trnou6 ; no translation, just return trnou1: lodsb ; get a char push si push cx call sndhst ; send the character pop cx pop si loop trnou1 ; loop thru rest of translation ret ; and return trnou2: test ax,fnkey ; function key? jz trnou5 ; no, keep going and ax,not fnkey ; turn off function bit. mov di,offset spckey ; our special keys mov cx,spclen ; length of special key table repne scasw ; look for it in our table jne trnou3 ; not found, maybe arrow key... sub di,offset spckey+2 ; get index call spchnd[di] ; call appropriate handler ret ; and return trnou3: mov di,offset arrkey ; look for an arrow-type key... mov cx,arrlen ; length of arrow key table repne scasb ; is it an arrow key? jne trnou4 ; no, forget it sub di,offset arrkey+1 ; get index into table shl di,1 ; double for word index mov si,arrtrn[di] ; get translation mov cl,[si] inc si mov ch,0 jmp trnou1 ; go send translation trnou4: mov di,offset keypad ; look for a keypad key. mov cx,keypln repne scasb ; is it in keypad? jne trnou6 ; no, forget it sub di,offset keypad+1 add di,keyptr ; index into correct translation table mov al,[di] ; get translation cmp akeyflg,0 ; in alternate keypad mode? je trnou5 ; no, just send the char push ax ; else save the character mov al,esc call sndhst mov al,'O' call sndhst ; send prefix pop ax ; get the character back and fall thru... trnou5: call sndhst ; send the character trnou6: ret trnout endp ; handle the print screen key prtscr proc near push ds ; save data segment mov ax,scrseg mov ds,ax ; address screen segment mov cx,slen ; # of lines on screen mov bx,0 ; current line # prtsc1: push cx ; save counter push bx ; and line ptr mov si,ds:[latofs+bx] ; get ptr to line mov cx,swidth ; max # of chars/line mov di,offset prbuf ; print buffer prtsc2: lodsb ; get a byte or al,al ; is it a null? jne prtsc3 ; no, go on mov al,' ' ; yes, replace by space prtsc3: stosb ; drop it off cmp al,' ' ; is it a space? je prtsc4 ; yes, go on mov dx,cx ; else remember count at last non-space prtsc4: cmp al,0ffH ; end of line? loopne prtsc2 ; continue if not end mov cx,dx ; count at last non-space, plus 1 neg cx add cx,swidth+1 ; figure out # of chars to print mov dx,offset prbuf push ds ; save this temporarily mov ax,es mov ds,ax ; address data segment to print jcxz prtsc5 ; 0 length, keep going mov bx,4 ; standard printer device mov ah,writef2 ; write call int dos ; write to the printer prtsc5: mov ah,writef2 mov bx,4 mov dx,offset crlf mov cx,2 int dos ; follow line with a crlf pop ds pop bx pop cx ; restore counters add bx,2 ; point to next line loop prtsc1 ; and keep going pop ds ; restore registers ret ; and return prtscr endp ; toggle print flag... togprt proc near xor ourflgs,fpscr ; toggle flag ret ; and return togprt endp ; Send a character to the host, handle local echo sndhst proc near push ax ; save the character mov ah,al call outchr nop nop nop pop ax test ourarg.flgs,lclecho ; echoing? jz sndhs2 ; no, exit cmp al,lf ; scrolling? *** jne sndhs1 ; no, go on call scrprep mov al,lf sndhs1: int fastcon sndhs2: ret ; and return sndhst endp ; print a message to the screen. Returns normally. tmsg proc near mov ah,prstr int dos ret tmsg endp ; save the screen for later savscr proc near push ds mov ax,scrseg mov ds,ax mov cx,slen ; # of lines to do mov bx,0 ; current line # mov di,offset ourscr ; place to save screen mov dx,offset ourattr ; and to save attributes savsc1: push cx ; save current count mov si,ds:[latofs+bx] ; get line ptr mov cx,swidth ; # of chars/line rep movsb ; copy it out mov si,ds:[latofs+bx] add si,1000H ; this is where attributes start xchg dx,di ; this holds attribute ptr mov cx,swidth ; # of attrs to move rep movsb xchg dx,di pop cx ; restore counter add bx,2 ; increment line ptr loop savsc1 ; save all lines and attributes pop ds call savpos ; might as well save cursor pos ret savscr endp ; restore the screen saved by savscr rstscr proc near call cmblnk ; start by clearing screen mov si,offset ourscr ; point to saved screen mov dx,offset ourattr ; and attributes mov cx,slen ; # of lines/screen mov bx,101H ; start at top left corner rstsc1: push bx push cx push si ; save ptrs push dx mov ax,si ; this is source call prlina ; print the line pop dx pop si pop cx pop bx add si,swidth ; point to next line add dx,swidth ; and next attributes inc bx ; address next line loop rstsc1 ; keep restore lines call rstpos ; don't forget position ret rstscr endp ; circular buffer management for screen. ; for these to work correctly, the buffer size MUST be a multiple ; of the screen width. ; put a line into the circular buffer. Pass the buffer structure ; in bx, the pointer to the line in ax. putcirc proc near push si push di push cx push dx mov di,[bx].pp ; pick up buffer ptr add di,swidth ; increment to next avail slot cmp di,[bx].bend ; past end? jb putci1 ; no, leave alone mov di,[bx].orig ; else start at beginning putci1: mov [bx].pp,di ; update ptr mov si,ax ; this is source mov cx,swidth rep movsb ; copy into buffer cmp [bx].lcnt,npgs*slen ; can we increment it? jae putci2 ; no, keep going inc [bx].lcnt ; else count this line putci2: pop dx pop cx pop di pop si ; restore registers ret putcirc endp ; get a line from the circular buffer, removing it from the buffer. ; returns with carry on if the buffer is empty. ; pass the buffer structure in bx, the buffer to copy the line into ; in ax. getcirc proc near push si push di push cx push dx cmp [bx].lcnt,0 ; any lines in buffer? jne getci1 ; yes, ok to take one out. stc ; else set carry jmp short getcir3 ; and return getci1: mov si,[bx].pp ; this is source mov di,ax ; this is dest mov cx,swidth ; # of chars to copy rep movsb mov si,[bx].pp ; get ptr again sub si,swidth ; move back cmp si,[bx].orig ; compare to origin jae getcir2 ; still in range, continue mov si,[bx].bend ; else use end of buffer sub si,swidth-1 ; minus length of a piece getcir2:mov [bx].pp,si ; update ptr dec [bx].lcnt ; decrement # of lines in buffer clc ; make sure no carry getcir3:pop dx pop cx pop di pop si ret getcirc endp ; prepares for scrolling by saving the top line in topbuf. scrprep proc near push ds ; preserve data segment mov ax,scrseg mov ds,ax ; address screen segment cmp byte ptr [ds:csrlin],slen ; are we at the bottom? pop ds ; restore in case we're returning... jne scrpr1 ; no, don't save it ; alternate entry that doesn't check if we're on the bottom row. savtop: push es push ds mov ax,ds mov es,ax mov ax,scrseg mov ds,ax ; address screen segment mov si,ds:word ptr [l1ptr] ; get ptr to top line mov di,offset tlbuf ; this is where it goes mov cx,swidth ; # of bytes to copy rep movsb ; get the top line pop ds pop es ; restore segments mov bx,offset topbuf ; top buffer ptr mov ax,offset tlbuf ; this is where line is now call putcirc ; put into circular buffer scrpr1: ret ; and return scrprep endp ; get the screen's bottom line into the buffer in ax. getbot proc near push es push ds push ax mov ax,ds mov es,ax mov ax,scrseg mov ds,ax mov si,ds:word ptr [llptr] ; get ptr to bottom line pop di ; destination is on stack mov cx,swidth ; # of bytes to copy rep movsb ; get the top line pop ds ; restore segments pop es ret getbot endp ; handle the previous screen button... prvscr proc near mov ax,offset tlbuf mov bx,offset topbuf call getcirc jc prvsc3 ; no lines, forget it call savpos ; save cursor position mov ax,offset botbuf ; place to put screen mov bx,slen ; else just use last line on screen mov dx,-1 ; move backwards call rolscr ; save current screen call cmblnk ; clear screen mov cx,slen ; # of lines per screenfull prvsc1: mov bl,cl ; this is current line mov bh,1 ; this is column mov ax,offset tlbuf ; where to get the line from push cx ; save count call prlin ; put the line on the screen mov ax,offset tlbuf mov bx,offset topbuf call getcirc ; get another line pop cx jc prvsc2 ; no more, exit loop loop prvsc1 ; loop for all lines prvsc2: call rstpos ; restore screen position prvsc3: ret ; and return prvscr endp ; handle the next screen button... nxtscr proc near mov ax,offset tlbuf mov bx,offset botbuf call getcirc ; get a line from the bottom jc nxtsc3 ; no lines, forget it call savpos ; save cursor pos mov ax,offset topbuf ; place to put screen mov bx,1 ; start with first line mov dx,1 ; move backwards call rolscr ; save current screen call cmblnk ; clear screen mov cx,slen ; # of lines per screenfull nxtsc1: mov bl,slen+1 sub bl,cl ; this is current line mov bh,1 ; this is column mov ax,offset tlbuf ; where to get the line from push cx ; save count call prlin ; put the line on the screen mov ax,offset tlbuf ; where to put the next line mov bx,offset botbuf call getcirc ; try to get another pop cx jc nxtsc2 ; no more, break loop loop nxtsc1 ; loop for all lines nxtsc2: call rstpos ; restore cursor position nxtsc3: ret ; and return nxtscr endp ; save a screen by rolling them into a circular buffer. ; enter with ax/ circular buffer ptr, bx/ first line to get ; dx/ increment rolscr proc near shl dx,1 ; double increment for word ptr dec bx ; ptr starts at 0 shl bx,1 ; convert to word ptr mov cx,slen ; # of lines to save rolsc1: push cx push dx push bx push ax push ds mov ax,scrseg mov ds,ax ; address screen mov si,ds:[latofs+bx] ; get current line mov di,offset rlbuf ; place to put it mov cx,swidth ; # of bytes to move rep movsb ; get the lne pop ds ; restore segment pop bx ; this is desired circ buffer ptr mov ax,offset rlbuf ; this is where the line is call putcirc ; save in circular buffer mov ax,bx ; put buffer ptr back where it belongs pop bx ; get line pos back pop dx ; and increment pop cx ; don't forget counter add bx,dx ; move to next line loop rolsc1 ; loop thru all lines ret ; and return rolscr endp ; move screen down a line, get one previous line back prvlin proc near ; get the previous line back mov ax,offset tlbuf ; place to put line temporarily mov bx,offset topbuf ; where to get lines from call getcirc ; try to get a line jc prvli1 ; no more, just return mov ax,offset blbuf ; place for bottom line call getbot ; fetch bottom line mov ax,offset blbuf mov bx,offset botbuf call putcirc ; save in circular buffer call savpos ; save cursor position mov dx,offset topdwn ; home, then reverse index call tmsg mov ax,offset tlbuf ; point to data mov bx,0101H ; print line at top of screen call prlin ; print the line call rstpos ; restore cursor position prvli1: ret ; and return prvlin endp ; move screen up a line, get one bottom line back nxtlin proc near mov ax,offset blbuf ; place to put line temporarily mov bx,offset botbuf ; where to get lines from call getcirc ; try to get a line jc nxtli1 ; no more, just return call savtop ; save one line off of top call savpos ; save cursor position mov dx,offset botup ; go to bottom, then scroll up a line call tmsg mov ax,offset blbuf ; point to data mov bx,0100H + slen ; print at bottom line call prlin ; print the line call rstpos ; restore cursor position nxtli1: ret ; and return nxtlin endp ; save cursor position savpos proc near mov dx,offset curinq ; where is the cursor? call tmsg mov posbuf,esc ; put an escape in the buffer first mov di,offset posbuf+1 savpo1: mov ah,8 ; read, no echo int dos cmp al,'R' ; end of report? je savpo2 ; yes stosb ; no, save it jmp savpo1 ; and go on savpo2: mov al,'H' ; this ends the sequence when we send it stosb mov byte ptr [di],'$' ; need this to print it later ret ; and return savpos endp ; restore the position saved by savpos rstpos proc near mov dx,offset posbuf call tmsg ; just print this ret ; and return rstpos endp ; print a ff-terminated line at most swidth long... Pass the line in ax. ; cursor position should be in bx. ; prlina writes attributes as well, which should be passed in dx. prlin proc near mov bp,2 ; print characters only jmp short prli1 prlina: xor bp,bp ; 0 means print attributes as well. prli1: push es ; this trashes es!!! mov si,ax ; better place for ptr mov di,ax ; need it here for scan mov cx,swidth ; max # of chars in line mov al,0ffh ; this marks the end of the line repne scasb ; look for the end jne prli2 ; not found inc cx ; account for pre-decrement prli2: neg cx add cx,swidth ; figure out length of line jcxz prli3 ; 0-length line, skip it. mov ax,2 ; writing characters mov bp,ds ; wants segment here mov di,14H ; fast write to screen int firmwr ; pos is in bx, char ptr in si prli3: pop es ; restore register ret ; and return prlin 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 //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 640 msxrb.asm /bin/echo -n ' '; /bin/ls -ld msxrb.asm fi /bin/echo 'Extracting msxsys.doc' sed 's/^X//' <<'//go.sysin dd *' >msxsys.doc SPECIFICATION FOR KERMIT SYSTEM-DEPENDENT MODULES by Jeff Damens, Columbia University All the system-independent global data structures used in Kermit-MS are defined in the file MSDEFS.H. The routine MSXxxx.ASM contains system-dependent support for system xxx, except for terminal emulation, which is in MSXxxx.ASM, described below. The routines in the MSX module may change any registers but the stack pointer and segment registers, unless otherwise noted. A routine that returns via a RET instruction is said to return normally; a routine that skip returns is one that returns to three bytes past the normal return address. Global variables that must be defined in the system-dependent module: XXOFSNT byte. This should be set to a non-zero value if we are doing flow control and have sent an XOFF character to the remote host, zero otherwise. MACHNAM byte. A $-terminated string identifying the machine this version of Kermit is for; it is printed when Kermit starts up. SETKTAB byte. A keyword table associating terminal key names to 16-bit scan code values, used in the set key command. If the kermit version can accept arbitrary decimal values as scan codes, the word "SCAN" should appear in the table with a scan value of -1. If key redefinition is not implemented, the first byte of the table should be a zero. SETKHLP byte. A $-terminated string to be printed when ? is typed in the SET KEY command. This is usually simply a list of the key names in SETKTAB. SETKHLP must be defined even if key redefinition is not implemented, to satisfy the linker; if key redefinition is not implemented, SETKHLP will never be displayed. COUNT word. The number of characters in the serial input buffer, if known. This is how Kermit knows to send an XON if the serial handler has sent an XOFF. If the number of characters in the buffer isn't known, COUNT should be 0. These are the required entry points for the system dependent dependent module MSXxxx.ASM. SERINI Parameters None. Returns Normally, no return value. Description Perform any initialization that must be done before the serial port can be used, including setting baud rate, interrupt vectors, etc. Parity and baud rate should be set according to the values in the PORTINFO structure. The external variable PORTVAL points to the PORTINFO structure for the current port. Calling SERINI more than once without an intervening call to SERRST should have no effect. SERRST Parameters None. Returns Normally, no return value. Description Undoes any initialization done by SERINI, including resetting the serial port, restoring any interrupt vectors changed by SERINI, etc. Calling this more than once without an intervening call to SERINI should be harmless. CLRBUF Parameters None. Returns Normally, no return value. Description Remove and discard from the serial port's input buffer any characters sent by the remote host that have not yet been read by Kermit, and set COUNT to 0. This is used before a file transfer to flush NAK's that accumulate in the buffer when the remote host is in server mode. OUTCHR Parameters A character in AH. Returns Skip returns if the character has been transmitted; returns normally if the character can not be transmitted because of a hardware error. Description Sends the character in AH out the currently selected serial port. OUTCHR can assume that SERINI will have been called previously. OUTCHR should call the external routine DOPAR to set the parity of the character if the communications hardware doesn't automatically set parity. Flow control should be honored; the external variable PORTVAL contains a pointer to a PORTINFO structure (as defined in MSDEFS.H) containing the current flow control definitions. COMS Parameters None. Returns Normally if a parse error is encountered, skip returns otherwise. Description Called by the SET PORT command. On a machine with multiple serial ports, COMS should parse for the name or number of a serial port and make that the port used by succeeding calls to SERINI, PRTCHR, OUTCHR, and SERRST. It should set the external variable PORTVAL to point to one of the external port structures PORT1 or PORT2, and set COMFLG in the FLAGS structure to 1 for port one, 0 for port 2. For implementations that use only one serial port, COMS should print a message to that effect and skip return. VTS Parameters None. Returns Normally if a parse error is encountered, skip returns otherwise. Description Parses for an ON or OFF, sets HEATH-19 emulation while in terminal emulation appropriately. The VTFLG field of the FLAGS structure should be set non-zero if HEATH-29 emulation is on, zero otherwise. If HEATH-19 emulation is not done, VTS should print a message and skip return. DODEL Parameters None. Returns Normally, no return value. Description Erases the character immediately to the left of the cursor from the screen, then backs up the cursor. CTLU Parameters None. Returns Normally, no return value. Description Move the cursor to the left margin, then clear the line. CMBLNK Parameters None. Returns Normally, no return value. Description Clears the screen and homes the cursor. LOCATE Parameters None. Returns Normally, no return value. Description Homes the cursor. LCLINI Parameters None. Returns Normally, no return value. Description Performs any system-dependent initialization required by this implementation. PRTCHR Parameters None. Returns Normally, with the next character from the currently selected serial port in AL. Skip returns if no character is available. Description Reads the next character from the current serial port. PRTCHR can assume SERINI has been called previously, and should handle flow control correctly. DOBAUD Parameters None. Returns Normally, no return value. Description Sets the baud rate for the current port. The baud rate should be obtained from the BAUD field of the PORTINFO structure, pointed to by the external variable PORTVAL. CLEARL Parameters None. Returns Normally, no return value. Description Clears from the cursor to the end of the current line. DODISK Parameters None. Returns Normally, no return value. Description Sets the external variable DRIVES to the number of disk drives attached to the machine. GETBAUD Parameters None. Returns Normally, no return value. Description Store current baud rate of the currently selected port in the BAUD field of the current PORTINFO structure, which is pointed to by PORTVAL. If the baud rate is to default to a particular value, this routine can store that value into the BAUD field instead. BEEP Parameters None. Returns Normally, no return value. Description Rings the terminal bell. PUTHLP Parameters A pointer to a string in AX. Returns Normally, no return value. Description Writes the null-terminated string given in AX to the terminal. This is used to display help and status messages. The IBM and Rainbow versions write the string in a reverse video box. PUTMOD Parameters A pointer to a string in AX. Returns Normally, no return value. Description Writes the null-terminated string given in AX to the last line of the screen, in inverse video if possible. CLRMOD Parameters None. Returns Normally, no return value. Description Clears the line written by PUTMOD. POSCUR Parameters Row in DH, column in DL. Returns Normally, no return value. Description Positions the cursor to the row and column given in DX. Rows and columns both originate at 0 (not 1!). SENDBR Parameters None. Returns Normally, no return value. Description Send a break to the current serial port. SHOWKEY Parameters Pointer to a terminal argument block in AX (see TERM below). Returns Normally, with a string pointer in AX and the length of the string in CX. Description Called by the SHOW KEY command. Reads a key from the terminal and returns a string containing implementation-dependent information about the key. In the usual case, the string contains the key's (machine-dependent) scan code, and the key's definition (if any) from the terminal argument block. The length of the returned string should be returned in CX. The string may contain any characters; unprintable characters will be quoted when the string is printed. If the implementation does not support key redefinition, SHOWKEY may return a static string saying so. TERM Parameters Pointer to terminal argument block in AX. Returns Normally, no return value. Description Do terminal emulation, based on argument block described below... The terminal emulator is supplied in the file MSYxxx.ASM. The terminal argument block passed to the terminal emulator has the following fields: FLGS Byte containing flags. Flags are: SCRSAM (80H) If on, the terminal emulator shouldn't re-display the screen when entered. CAPT (40H) Capture output. If on, the routine passed in field CAPTR is called with each character sent to the screen. EMHEATH (20H) Emulate a Heath-19 terminal if on. HAVTT (10H) A key redefinition table is present. TRNCTL (08H) Print control character X as ^X (useful for debugging). MODOFF (04H) Do not display emulator mode line if on. LCLECHO (01H) Echo keyboard characters on the screen in addition to sending them to the port. PRT Port to use for terminal emulation, used only in mode line. This is just a copy of COMFLG in FLAGS. COLS Number of columns on screen. ROWS Number of rows on screen. CAPTR Routine to call to with each character sent to the screen if CAPT flag is on. Characters are passed in AL. BELLD Bell divisor (used only on IBM). KLEN Number of keys in key redefinition table, if HAVTT flag is on. KTAB Address of key redefinition table. The key redefinition table is a table of KLEN 16-bit scan codes. Each (machine dependent) scan code represents a key that is redefined. KRPL Address of key replacement table. The key replacement table parallels the key redefinition table given in KTAB. Entries in the replacement table are 16-bit pointers to redefinitions. Each redefinition has a one-byte length, followed by the definition. ESCC Escape character (single byte). When this character is typed to the emulator, it should return. BAUDB byte. Bits describing the baud rate so it can be printed on the mode line. This is a copy of the BAUD field in the PORTINFO structure. Currently used only on the IBM. See MSDEFS.H for possible values. PARITY byte. Current parity to print on the mode line. This is a copy of PARFLG in the PORTINFO structure. Currently used only on the IBM. See MSDEFS.H for possible values. //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 640 msxsys.doc /bin/echo -n ' '; /bin/ls -ld msxsys.doc fi
knutson@ut-ngp.UUCP (Jim Knutson) (10/05/84)
: Run this shell script with "sh" not "csh" PATH=:/bin:/usr/bin:/usr/ucb export PATH all=FALSE if [ $1x = -ax ]; then all=TRUE fi /bin/echo 'Extracting msxwng.asm' sed 's/^X//' <<'//go.sysin dd *' >msxwng.asm public serini, serrst, clrbuf, outchr, coms, vts, dodel, ctlu public cmblnk, locate, lclini, prtchr, dobaud, clearl public dodisk, getbaud, beep, term, puthlp public count, poscur, machnam, sendbr, putmod, clrmod public setktab, setkhlp, xofsnt, showkey include msdefs.h false equ 0 true equ 1 ctrla equ 1 ; Control-A. auxin equ 3 auxout equ 4 auxfil equ 3 ; file number of aux file. iordy equ 6 ; input ready function write equ 40h wbios equ 88h settrp equ 02h clrtrp equ 03h chrrdy equ 01h txrdy equ 02h rcvdat equ 1080h rcvstat equ 1082h rcvmod equ 1084h rcvcmd equ 1086h trdat equ 1088h trmod equ 108ch wrcmd equ 108eh ; mode bits mod1 equ 4dh ; clock rate, 8 bits, 1 stop bit mod2 equ 30h ; internal clock ; command register bits txen equ 01h dtr equ 02h rxen equ 04h brk equ 08h clrerr equ 10h rts equ 20h datas segment public 'datas' extrn drives:byte, flags:byte, trans:byte extrn portval:word, port1:byte, port2:byte, swchar:byte portin db 0 crlf db cr,lf,'$' machnam db 'FIELD TEST Wang$' noimp db cr,lf,'Command not implemented.$' shkmsg db 'Not implemented.' shklen equ $-shkmsg xofsnt db 0 ; Say if we sent an XOFF. xofrcv db 0 ; Say if we received an XOFF. setktab db 0 setkhlp db 0 invseq db esc,'[7m$' ; Reverse video on. nrmseq db esc,'[0m$' ; Reverse video off. ivlseq db 79 dup (' '),cr,'$' ; Make a line inverse video comphlp db cr,lf,'1 (COM1) 2 (COM2)$' delstr db BS,' ',BS,'$' ; Delete string. clrlin db cr,esc,'[K$' tmp db ?,'$' temp dw 0 temp1 dw ? ; Temporary storage. temp2 dw ? ; Temporary storage. ; Entries for choosing communications port. [19b] comptab db 04H db 01H,'1$' dw 01H db 01H,'2$' dw 00H db 04H,'COM1$' dw 01H db 04H,'COM2$' dw 00H ; variables for serial interrupt handler source db bufsiz DUP(?) ; Buffer for data from port. bufout dw 0 ; buffer removal ptr count dw 0 ; Number of chars in int buffer. bufin dw 0 ; buffer insertion ptr telflg db 0 ; Are we acting as a terminal. [16] [17c] clreol db esc,'[0K$' blank db esc,'[H',esc,'[J$' movcur db esc,'[' colno db 20 dup (?) ten db 10 prthnd dw 0 ourarg termarg <> ; must parallel baud rate defs in pcdefs. baudtab db 0ffh ; 45.5 baud (not supported) db 0 ; 50 db 1 ; 75 db 2 ; 110 db 3 ; 134.5 db 4 ; 150 db 5 ; 300 db 6 ; 600 db 7 ; 1200 db 8 ; 1800 db 9 ; 2000 db 10 ; 2400 db 12 ; 4800 db 14 ; 9600 db 15 ; 19.2k db 0ffh ; 38.4k (ha) nbaud equ $-baudtab qid dw ? prtcnt dw ? trqid dw ? tmqid dw ? brflg db ? datas ends code segment public extrn comnd:near, dopar:near, prserr:near assume cs:code,ds:datas DODISK PROC NEAR mov ah,gcurdsk ; Current disk value to AL. int dos mov dl,al ; Put current disk in DL. mov ah,seldsk ; Select current disk. int dos ; Get number of drives in AL. mov drives,al ret DODISK ENDP ; Clear the input buffer before sending a packet. [20e] CLRBUF PROC NEAR mov ah,ioctl mov bx,auxfil mov al,iordy int dos cmp al,0ffh jne clrb1 ; not ready, keep going mov ah,auxin int dos jmp clrbuf ; read char and keep going. clrb1: mov count,0 mov ax,offset source mov bufin,ax mov bufout,ax ret CLRBUF ENDP ; Common routine to clear to end-of-line. [19a] CLEARL PROC NEAR mov dx,offset clreol mov ah,prstr int dos ret CLEARL ENDP ; This routine should set the baud rate for the current port but it ; is actually done in SERINI. dobaud proc near ret dobaud endp ; Send a break out the current serial port. Returns normally. sendbr: push dx push ax push cx push ds ; preserve data segment mov ax,cs mov ds,ax ; handler is in code segment mov al,settrp mov bx,txrdy ; interrupt on transmitter empty mov cx,0 ; interrupt immediately mov dx,offset sendb1 ; handler routine int wbios pop ds mov trqid,bx push ds mov ax,cs mov ds,ax mov al,settrp mov bx,0 ; 10 ms timer mov cx,21 ; after 21 times - approx 200 ms. mov dx,offset sendb2 ; timer interrupt int wbios pop ds mov tmqid,bx mov brflg,1 mov dx,rcvcmd in al,dx ; Read command register. or al,brk+txen ; Set send-break bit. mov dx,wrcmd ; Write command register. out dx,al pause: cmp brflg,0 jne pause ; while non-zero, keep going mov al,clrtrp ; clear the trap mov bx,trqid int wbios mov al,clrtrp mov bx,tmqid int wbios pop cx pop ax pop dx ret sendb1 proc far ret sendb1 endp sendb2 proc far push ax push ds mov ax,seg datas mov ds,ax mov brflg,0 mov dx,rcvcmd in al,dx and al,not (txen + brk) mov dx,wrcmd out dx,al pop ds pop ax ret sendb2 endp ; Write a line in inverse video at the bottom of the screen... ; the line is passed in dx, terminated by a $. Returns normally. putmod proc near push dx ; preserve message mov dx,24 * 100H ; line 24 call poscur mov dx,offset invseq ; put into inverse video mov ah,prstr int dos pop dx int dos mov dx,offset nrmseq ; normal videw int dos ret ; and return putmod endp ; Clear the mode line written by putmod. Returns normally. clrmod proc near mov dx,24 * 100H call poscur call clearl ret clrmod endp ; Put a help message one the screen in reverse video. Pass ; the message in AX, terminated by a null. Returns normally. ; The message is put wherever the cursor currently is located. puthlp proc near push ax mov ah,prstr ; Leave some room before the message. mov dx,offset crlf int dos mov dx,offset invseq ; Put into reverse video. int dos pop si ; Put message address here. puth0: mov ah,prstr mov dx,offset ivlseq ; Make line inverse video int dos puth1: lodsb cmp al,0 ; Terminated with a null. je puth2 mov dl,al mov ah,conout int dos cmp al,lf ; Line feed? je puth0 ; Yes, clear the next line. jmp puth1 ; Else, just keep on writing. puth2: mov dx,offset nrmseq ; Normal video. mov ah,prstr int dos mov dx,offset crlf int dos ret puthlp endp outchr: push dx ; Save register. mov al,ah call dopar mov dl,al mov ah,auxout int dos pop dx jmp rskp ; This routine blanks the screen. CMBLNK PROC NEAR mov ah,prstr mov dx,offset blank int dos ret CMBLNK ENDP LOCATE PROC NEAR mov dx,0 ; Go to top left corner of screen. jmp poscur ; callret... LOCATE ENDP GETBAUD PROC NEAR cld mov dx,rcvmod in al,dx in al,dx ; get second mode word and al,0fh ; isolate baud rate mov cx,nbaud mov di,offset baudtab mov bx,ds mov es,bx ; address correct segment mov bx,portval repne scasb ; look for baud rate jne getb1 ; mystery baud rate... sub di,offset baudtab + 1 mov [bx].baud,di ; store baud rate in comm area ret ; and return getb1: mov [bx].baud,-1 ; unknown baud rate ret GETBAUD ENDP ; skip returns if no character available at port, ; otherwise returns with char in al, # of chars in buffer in dx. PRTCHR PROC NEAR prtchx: cmp count,0 je prtch4 ; empty buffer, forget it. mov si,bufout lodsb cmp si,offset source + bufsiz jb prtch1 mov si,offset source prtch1: mov bufout,si dec count push bx mov bx,portval cmp [bx].parflg,PARNON ; no parity? je prtch3 ; then don't strip and al,7fh ; else turn off parity prtch3: mov dx,count ; chars left in buffer pop bx ret prtch4: jmp rskp ; no chars... PRTCHR ENDP ; Position the cursor according to contents of DX. POSCUR PROC NEAR mov ax,ds mov es,ax ; address data segment!!! cld mov di,offset colno mov al,dh ; row inc al call nout mov al,';' stosb mov al,dl ; col inc al call nout mov al,'H' stosb mov al,'$' stosb mov dx,offset movcur mov ah,prstr int dos ; print the sequence ret POSCUR ENDP NOUT PROC NEAR cbw ; extend to word div byte ptr ten ; divide by 10 or al,al ; any quotient? jz nout1 ; no, forget this push ax ; save current result call nout ; output high order pop ax ; restore nout1: mov al,ah ; get digit add al,'0' ; make printable stosb ret ; put in buffer and return NOUT endp ; Perform a delete. DODEL PROC NEAR mov ah,prstr mov dx,offset delstr ; Erase weird character. int dos ret DODEL ENDP ; Perform a Control-U. CTLU PROC NEAR mov ah,prstr mov dx,offset clrlin int dos ret CTLU ENDP COMS PROC NEAR mov dx,offset comptab mov bx,offset comphlp mov ah,cmkey call comnd jmp r push bx mov ah,cmcfm call comnd ; Get a confirm. jmp comx ; Didn't get a confirm. nop pop bx mov flags.comflg,bl ; Set the comm port flag. cmp flags.comflg,1 ; Using Com 1? jne coms0 ; Nope. mov ax,offset port1 mov portval,ax ret coms0: mov ax,offset port2 mov portval,ax ret comx: pop bx ret COMS ENDP VTS PROC NEAR jmp notimp VTS ENDP notimp: mov ah,prstr mov dx,offset noimp int dos jmp prserr lclini: mov trans.escchr,ctrla ; Use Control-A as escape char. mov swchar,'/' ret showkey: mov ax,offset shkmsg mov cx,shklen ret ; Common initialization for using serial port. SERINI PROC NEAR cmp portin,0 ; already inited? jne serin1 ; yes, skip it mov portin,1 ; remember inited mov dx,rcvcmd in al,dx ; read cmd register to reset mode ptr. mov dx,trmod mov al,mod1 out dx,al push bx mov bx,portval mov si,[bx].baud pop bx mov al,baudtab[si] or al,mod2 out dx,al mov dx,wrcmd mov al,txen+dtr+rxen+clrerr+rts out dx,al ; enable transmit and receive call clrbuf ; empty buffer mov al,settrp mov bx,chrrdy ; interrupt on character ready mov cx,0 ; interrupt immediately mov dx,offset serint ; handler routine mov prtcnt,0 ; no characters in yet push ds mov si,cs mov ds,si int wbios pop ds or al,al jne serin1 mov qid,bx ; preserve trap identification serin1: ret ; We're done. [21c] SERINI ENDP SERRST PROC NEAR cmp portin,0 ; already de-inited? je serrs1 ; yes, skip this mov portin,0 mov al,clrtrp mov bx,qid int wbios serrs1: ret SERRST ENDP ; serial interrupt handler serint proc far push ds push ax push dx push di mov ax,seg datas mov ds,ax mov di,bufin mov dx,rcvdat in al,dx mov [di],al inc di cmp di,offset source + bufsiz jb sernt1 mov di,offset source sernt1: mov bufin,di inc count pop di pop dx pop ax pop ds ret serint endp ; Generate a short beep. BEEP PROC NEAR mov dl,bell mov ah,conout int dos ret BEEP 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 term proc near mov si,ax ; this is source mov di,offset ourarg ; place to store arguments mov ax,ds mov es,ax ; address destination segment mov cx,size termarg rep movsb ; copy into our arg blk term1: call prtchr jmp short term2 ; have a char... nop nop jmp short term3 ; no char, go on term2: and al,7fh push ax mov dl,al mov ah,dconio int dos ; write out the character pop ax test ourarg.flgs,capt ; capturing output? jz term3 ; no, forget it call ourarg.captr ; else call the routine term3: mov ah,dconio mov dl,0ffh int dos or al,al jz term1 ; no character, go on cmp al,ourarg.escc ; escape char? je term4 ; yes, exit push ax ; save char mov ah,al call outchr ; output the character nop nop nop pop ax test ourarg.flgs,lclecho ; echoing? jz term1 ; no, continue loop mov dl,al mov ah,dconio int dos jmp term1 ; else echo and keep going term4: ret term endp code ends end //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 640 msxwng.asm /bin/echo -n ' '; /bin/ls -ld msxwng.asm fi /bin/echo 'Extracting msxz100.asm' sed 's/^X//' <<'//go.sysin dd *' >msxz100.asm ; Kermit system dependent module for Heath/Zenith Z100 public serini, serrst, clrbuf, outchr, coms, vts, dodel, public ctlu, cmblnk, locate, prtchr, dobaud, clearl, public dodisk, getbaud, beep, public count, xofsnt, puthlp, putmod, clrmod, poscur public sendbr, machnam, setktab, setkhlp, lclini, showkey include msdefs.h false equ 0 true equ 1 mntrgh equ bufsiz*3/4 ; High point = 3/4 of buffer full. ; constants used by serial port handler BRKBIT EQU 048H ; Send-break bit. MDMCOM1 EQU 00EFH ; Address of modem port command. [19b] ; external variables used: ; drives - # of disk drives on system ; flags - global flags as per flginfo structure defined in pcdefs ; trans - global transmission parameters, trinfo struct defined in pcdefs ; portval - pointer to current portinfo structure (currently either port1 ; or port2) ; port1, port2 - portinfo structures for the corresponding ports ; global variables defined in this module: ; xofsnt, xofrcv - tell whether we saw or sent an xoff. ; setktab - keyword table for redefining keys (should contain a 0 if ; not implemented) ; setkhlp - help for setktab. BIOS_SEG SEGMENT AT 40H ; Define segment where BIOS really is ORG 6*3 BIOS_AUXOUT LABEL FAR ; AUX output routine ORG 26*3 BIOS_AUXFUNC LABEL FAR ; AUX: function ORG 27*3 BIOS_CONFUNC LABEL FAR ; CON: function BIOS_SEG ENDS ; Function codes for BIOS_xxxFUNC CHR_WRITE EQU 0 ; Write character CHR_READ EQU 1 ; Read character CHR_STATUS EQU 2 ; Get status CHR_SFGS EQU 0 ; Get status subfunction CHR_SFGC EQU 1 ; Get config subfunction CHR_CONTROL EQU 3 ; Control function CHR_CFSU EQU 0 ; Set new configuration parameters CHR_CFCI EQU 1 ; Clear input buffer datas segment public 'datas' extrn drives:byte, flags:byte, trans:byte extrn portval:word, port1:byte, port2:byte setktab db 13 mkeyw 'F0',96h mkeyw 'F1',97h mkeyw 'F2',98h mkeyw 'F3',99h mkeyw 'F4',9ah mkeyw 'F5',9bh mkeyw 'F6',9ch mkeyw 'F7',9dh mkeyw 'F8',9eh mkeyw 'F9',9fh mkeyw 'F10',0a0h mkeyw 'F11',0a1h mkeyw 'SCAN',-1 setkhlp db cr,lf,'Keyname: f0, ... f11, "HELP" or "SCAN" follwed by ' db 'decimal scan code$' brkval db 0 ; What to send for a break. brkadr dw 0 ; Where to send it. badbd db cr,lf,'Unimplemented baud rate$' noimp db cr,lf,'Not implemented$' machnam db 'Heath-Zenith Z-100$' crlf db cr,lf,'$' delstr db BS,' ',BS,'$' ; Delete string. [21d] home db ESC,'H$' eeolstr db ESC,'K$' ; Erase to end of line clrstr db ESC,'E$' ; Erase entire display enamod db ESC,'x1$' ; Enable 25th line dismod db ESC,'y1$' ; Disable 25th line enascan db ESC,'y?$' ; Enable scan codes disscan db ESC,'x?$' ; Disable scan codes begrev db ESC,'p$' ; Enter reverse video endrev db ESC,'q$' ; Exit reverse video lin25 db ESC,'Y8 $' ; Column 1 row 25 savcur db ESC,'j$' ; Save current cursor position precur db ESC,'k$' ; Restore cursor to previous position clrlin db cr,'$' ; Clear line (just the cr part). xofsnt db 0 ; Say if we sent an XOFF. xofrcv db 0 ; Say if we received an XOFF. tmp db ?,'$' temp1 dw ? ; Temporary storage. ontab db 02H ; Two entries. db 03H,'OFF$' ; Should be alphabetized. [19a] dw 00H db 02H,'ON$' dw 01H ; this table is indexed by the baud rate definitions given in ; pcdefs. Unsupported baud rates should contain FF. bddat label word dw 0 ; 45.5 baud dw 1 ; 50 baud dw 2 ; 75 baud dw 3 ; 110 baud dw 4 ; 134.5 baud dw 5 ; 150 baud dw 6 ; 300 baud dw 7 ; 600 baud dw 8 ; 1200 baud dw 9 ; 1800 baud dw 10 ; 2000 baud dw 11 ; 2400 baud dw 12 ; 4800 baud dw 13 ; 9600 baud dw 14 ; 19200 baud dw 15 ; 38400 baud ; storage for port configuration cfginfo struc cfclass db 0 cfattr db 0 cfport dw 0 cfbaud db 0 cfhshk db 0 cfbctl db 0 cfecnt db 0 cfncnt db 0 cfnchr db 0 cfres db 6 dup(?) cfsize db 0 cfginfo ends auxconf cfginfo <> ; variables for serial interrupt handler count dw 0 ; Number of chars in int buffer. ourarg termarg <> shkbuf db 300 dup (?) ; room for definition shkmsg db ' Scan code: ' shkmln equ $-shkmsg shkms1 db cr,lf,' Definition: ' shkm1ln equ $-shkms1 datas ends code segment public extrn comnd:near, dopar:near, defkey:near assume cs:code,ds:datas ; local initialization lclini proc near mov brkval,BRKBIT ; What to send for a break. mov brkadr,MDMCOM1 mov flags.vtflg,0 ; Turn off true Heath mode (allows key macros) ret lclini endp ; this is called by Kermit initialization. It checks the ; number of disks on the system, sets the drives variable ; appropriately. Returns normally. DODISK PROC NEAR mov ah,gcurdsk ; Current disk value to AL. int dos mov dl,al ; Put current disk in DL. mov ah,seldsk ; Select current disk. int dos mov drives,al ret DODISK ENDP ; show the definition of a key. The terminal argument block (which contains ; the address and length of the definition tables) is passed in ax. ; Returns a string to print in AX, length of same in CX. ; Returns normally. showkey proc near push es push ax ; save the ptr mov bx,ds mov es,bx ; address data segment cld showk1: mov ah,prstr mov dx,offset enascan ; enable scan codes int dos mov ah,0ch ; char input with buffer flush mov al,7 int dos ; mov ah,chr_control ; mov al,chr_cfci ; clear input ; call bios_confunc ; mov ah,chr_read ; call bios_confunc ; read a char push ax mov ah,prstr mov dx,offset disscan ; disable scan codes int dos pop ax ; push ax ; save the character ; call gss ; get shift state ; pop bx mov ah,0 ; shift state to ah ; mov al,bh ; scan code to al push ax ; remember scan code mov di,offset shkbuf mov si,offset shkmsg mov cx,shkmln rep movsb ; copy in initial message call nout ; write out scan code mov si,offset shkms1 mov cx,shkm1ln ; second message rep movsb pop ax ; get scan code back pop bx ; and terminal arg block mov cx,[bx].klen ; and length jcxz showk2 ; no table, not defined push di ; remember output ptr mov di,[bx].ktab ; get key table repne scasw ; search for a definition for this mov si,di ; remember result ptr pop di ; get output ptr back jne showk2 ; not defined, forget it sub si,[bx].ktab ; compute offset from beginning sub si,2 ; minus 2 for pre-increment add si,[bx].krpl ; get index into replacement table mov si,[si] ; pick up replacement mov cl,[si] ; get length mov ch,0 inc si rep movsb ; copy into buffer showk2: mov ax,offset shkbuf ; this is buffer mov cx,di sub cx,ax ; length pop es ret ; and return showkey endp ; Clear the input buffer. This throws away all the characters in the ; serial interrupt buffer. This is particularly important when ; talking to servers, since NAKs can accumulate in the buffer. ; Returns normally. CLRBUF PROC NEAR cli mov ah,chr_control mov al,chr_cfci call bios_auxfunc mov count,0 sti ret CLRBUF ENDP ; Clear to the end of the current line. Returns normally. CLEARL PROC NEAR mov ah,prstr mov dx,offset eeolstr ; Erase to end of line int dos ret CLEARL ENDP ; Put the char in AH to the serial port. This assumes the ; port has been initialized. Should honor xon/xoff. Skip returns on ; success, returns normally if the character cannot be written. outchr: mov bp,portval cmp ds:[bp].floflg,0 ; Are we doing flow control. je outch2 ; No, just continue. xor cx,cx ; clear counter outch1: cmp xofrcv,true ; Are we being held? jne outch2 ; No - it's OK to go on. loop outch1 ; held, try for a while mov xofrcv,false ; timed out, force it off and fall thru. outch2: push dx ; Save register. mov al,ah ; Parity routine works on AL. call dopar ; Set parity appropriately. call bios_auxout pop dx jmp rskp ; This routine blanks the screen. Returns normally. CMBLNK PROC NEAR mov ah,prstr mov dx,offset clrstr int dos ret CMBLNK ENDP ; Locate: homes the cursor. Returns normally. LOCATE PROC NEAR mov ah,prstr mov dx,offset home ; Go to top left corner of screen. int dos LOCATE ENDP ; write a line in inverse video at the bottom of the screen... ; the line is passed in dx, terminated by a $. Returns normally. putmod proc near push dx ; preserve message mov ah,prstr mov dx,offset savcur int dos mov dx,offset enamod int dos mov dx,offset lin25 int dos mov dx,offset begrev int dos pop dx ; get message back int dos ; write it out mov dx,offset endrev int dos mov dx,offset precur int dos ret ; and return putmod endp ; clear the mode line written by putmod. Returns normally. clrmod proc near mov ah,prstr mov dx,offset dismod int dos ret clrmod endp BEEP PROC NEAR mov dl,07 ; ASCII BEL mov ah,dconio int dos ; Ring it ret BEEP ENDP ; put a help message on the screen. This one uses reverse video... ; pass the message in ax, terminated by a null. Returns normally. puthlp proc near push ax ; preserve this mov ah,prstr mov dx,offset crlf int dos pop si ; point to string again puthl3: lodsb ; get a byte cmp al,0 ; end of string? je puthl4 ; yes, stop mov dl,al mov ah,dconio int dos jmp puthl3 ; and keep going puthl4: mov ah,prstr mov dx,offset crlf int dos ret puthlp endp ; Set the baud rate for the current port, based on the value ; in the portinfo structure. Returns normally. DOBAUD PROC NEAR mov bp,portval mov temp1,ax ; Don't overwrite previous rate. [25] mov ax,ds:[bp].baud ; Check if new rate is valid. [25] mov tmp,2 mul tmp ; Get index into baud table. mov bx,offset bddat ; Start of table. add bx,ax mov ax,[bx] ; The data to output to port. cmp ax,0FFH ; Unimplemented baud rate. jne dobd0 mov ax,temp1 ; Get back orginal value. mov ds:[bp].baud,ax ; Leave baud rate as is. mov ah,prstr mov dx,offset badbd ; Give an error message. int dos ret dobd0: push ax ; Save it mov bx,ds ; Set up pointer to config info mov es,bx ; . . . mov bx,offset auxconf ; . . . mov ah,chr_status mov al,chr_sfgc ; get current config info call bios_auxfunc pop ax ; get baud back mov auxconf.cfbaud,al mov ah,chr_control ; Function is control mov al,chr_cfsu ; Subfunction is set new config call bios_auxfunc ; Set the configuration ret DOBAUD ENDP ; Get the current baud rate from the serial card and set it ; in the portinfo structure for the current port. Returns normally. ; This is used during initialization. GETBAUD PROC NEAR mov bx,ds mov es,bx mov bx,offset auxconf mov ah,chr_status mov al,chr_sfgc ; Status function get config info call bios_auxfunc mov ch,0 mov cl,auxconf.cfbaud mov bp,portval mov ds:[bp].baud,cx ret GETBAUD ENDP ; skip returns if no character available at port, ; otherwise returns with char in al, # of chars in buffer in dx. PRTCHR PROC NEAR call chkxon ; see if we need to xon push bx mov ah,chr_status mov al,chr_sfgs ; Status function get status call bios_auxfunc cmp bl,0 jnz prtch2 pop bx jmp rskp ; No data - check console. prtch2: mov dh,0 mov dl,bl ; Place # of chars in dx mov ah,chr_read call bios_auxfunc dec dl ; Decrement number of chars mov count,dx ; Save count for posterity pop bx ret PRTCHR ENDP ; local routine to see if we have to transmit an xon chkxon proc near push bx mov bx,portval cmp [bx].floflg,0 ; doing flow control? je chkxo1 ; no, skip all this cmp xofsnt,false ; have we sent an xoff? je chkxo1 ; no, forget it cmp count,mntrgh ; below trigger? jae chkxo1 ; no, forget it mov ax,[bx].flowc ; ah gets xon call outchr ; send it nop nop nop ; in case it skips mov xofsnt,false ; remember we've sent the xon. chkxo1: pop bx ; restore register ret ; and return chkxon endp ; Send a break out the current serial port. Returns normally. SENDBR PROC NEAR push cx push dx push ax xor cx,cx ; Clear loop counter. mov dx,brkadr ; Port address. [19b] in al,dx ; Get current setting. or al,brkval ; Set send-break bit(s). out dx,al ; Start the break. pause: loop pause ; Wait a while. xor al,brkval ; Clear send-break bit(s). out dx,al ; Stop the break. pop ax pop dx pop cx ret ; And return. SENDBR ENDP ; Position the cursor according to contents of DX: ; DH contains row, DL contains column. Returns normally. POSCUR PROC NEAR push dx mov ah,CONOUT mov dl,ESC int dos mov dl,'Y' int dos pop dx push dx mov dl,dh add dl,' ' int dos pop dx add dl,' ' int dos ret POSCUR ENDP ; Delete a character from the terminal. This works by printing ; backspaces and spaces. Returns normally. DODEL PROC NEAR mov ah,prstr mov dx,offset delstr ; Erase weird character. int dos ret DODEL ENDP ; Move the cursor to the left margin, then clear to end of line. ; Returns normally. CTLU PROC NEAR mov ah,prstr mov dx,offset clrlin int dos call clearl ret CTLU ENDP ; set the current port. COMS PROC NEAR jmp notimp COMS ENDP ; Set heath emulation on/off. VTS PROC NEAR mov dx,offset ontab mov bx,0 mov ah,cmkey call comnd jmp r push bx mov ah,cmcfm call comnd ; Get a confirm. jmp vt0 ; didn't get a confirm. nop pop bx mov flags.vtflg,bl ; Set the Heath emulation flag ret vt0: pop bx ret VTS ENDP notimp: mov ah,prstr mov dx,offset noimp int dos jmp rskp ; initialization for using serial port. This routine performs ; any initialization necessary for using the serial port, including ; setting up interrupt routines, setting buffer pointers, etc. ; Doing this twice in a row should be harmless (this version checks ; a flag and returns if initialization has already been done). ; SERRST below should restore any interrupt vectors that this changes. ; Returns normally. SERINI PROC NEAR ret ; We're done. SERINI ENDP ; Reset the serial port. This is the opposite of serini. Calling ; this twice without intervening calls to serini should be harmless. ; Returns normally. SERRST PROC NEAR ret ; All done. SERRST ENDP ; put the number in ax into the buffer pointed to by di. Di is updated nout proc near mov dx,0 ; high order is always 0. mov bx,10 div bx ; divide to get digit push dx ; save remainder digit or ax,ax ; test quotient jz nout1 ; zero, no more of number call nout ; else call for rest of number nout1: pop ax ; get digit back add al,'0' ; make printable stosb ; drop it off ret ; and return nout 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 //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 640 msxz100.asm /bin/echo -n ' '; /bin/ls -ld msxz100.asm fi /bin/echo 'Extracting msyibm.asm' sed 's/^X//' <<'//go.sysin dd *' >msyibm.asm title term public term, gss ; entry points include msdefs.h ; some character definitions chesc equ 27 bel equ 7 print_out equ 05h ; dos function to print to printer pbout equ 02h ; dos function to print a character prscan equ 72h ; print-screen scan code... upscan equ 49h ; up page dnscan equ 51h ; down page ctlup equ 84h ; ctl-up page ctldn equ 76h ; ctl-down page homscn equ 47h ; home screen endscn equ 4fh ; end of screen screen equ 10h ; bios screen call kb equ 16h ; keyboard interrupt alt_shift equ 8H ; alt shift key down ctl_shift equ 4H ; ctl key down left_shift equ 2H ; left shift key down right_shift equ 1H ; right shift key down timer equ 40h ; timer port bel_prt equ 61h ; speaker control crt_status equ 3dah ; crt status port disp_enb equ 8 ; display enable bit uparr equ 48h ; scan codes for arrow keys dnarr equ 50h lftarr equ 4bh rgtarr equ 4dh modfrm struc ; format of mode line db 'Esc chr: ' m_echr db 2 dup (?) db ', Port: ' m_prt db 1 dup (?) db ', Speed: ' m_baud db 4 dup (?) db ', Parity: ' m_par db 4 dup (?) db ', Echo: ' m_echo db 3 dup (?) db ', Type ' m_hlp db 2 dup (?) db '? for Help' modfrm ends datas segment public 'datas' waste db 100h dup (?) ;*** need this junk because assembler ;*** generates non-relocatable offsets ;*** for things like ;*** "sub di,offset foo" ;*** if offset foo < 100H ; stuff for screen routines flags db ? ; status flags... flags1 db 0 ; internal flags. prtscr equ 80h ; print screen pressed lnwrap equ 40h ; line wrap enabled. inited equ 08h ; been here before... cursor dw ? esc_ch db ? argadr dw ? ; address of arg blk ckeys db 0,prscan,dnscan,upscan,endscn,homscn,ctlup,ctldn db uparr,dnarr,lftarr,rgtarr lckeys equ $-ckeys ; ckacts must parallel ckeys above... ckacts dw trnbrk,trnprs,upwpg,dnwpg,endwnd,homwnd,dnwind,upwind dw trnupw,trndnw,trnlfw,trnrgw uptrn db esc,'A' dntrn db esc,'B' rgtrn db esc,'C' lftrn db esc,'D' spctab db chesc,cr,lf,bs,tab,bel lspctab equ $-spctab spcjmp dw outesc,outcr,outlf,outbs,outtab,outbel ; must match spctab esctab db 'YABCDEFGHIJKLM' db 'NOZ@[pq<vw' lesctab equ $-esctab ; escjmp must parallel esctab above escjmp dw movcur,curup,curdwn,currt,outbs,clrscr,outign,outign,curhom dw revind,clreow,clreol,inslin,dellin,delchr,noins dw vtident,entins,doansi dw invvid,nrmvid,outign,dowrap,nowrap vtidstr db chesc,'/K' lvtidst equ $-vtidstr coord dw ? insmod db ? wcoord dw ? ttstate dw outtt0 curattr db ? ; current attribute ansarg db ? ; ansi argument value igncnt db ? ; # of chars to ignore beldiv dw 2dch ; 550 hz? crt_mode db ? crt_cols db ? crt_lins db ? low_rgt dw ? ; lower right corner of window ; key redefinitions ktrntab dw ? ; address of translation table krpltab dw ? ; address of replacement table tmptab db 0eh,3bh ; scan code for bs, f1 ktlen dw ? modbuf modfrm <> ; mode line buffer ; routine to call for captured output captrtn dw ? ; some static data for mode line unkbaud db 'Unk ' ; must be 4 chars... baudn db '45.5' db ' 50' db ' 75' db ' 110' db ' 135' db ' 150' db ' 300' db ' 600' db '1200' db '1800' db '2000' db '2400' db '4800' db '9600' baudnsiz equ 14 ; # of baud rates known (tbl size / 4) parnams db 'Even' db 'Mark' db 'None' db 'Odd ' ; must be 4 chars db 'Spc ' offmsg db 'Off' onmsg db 'On ' lclmsg db 'Lcl' remmsg db 'Rem' ; storage for multi-window stuff swidth equ 80 slen equ 24 npgs equ 5 ; # of pages on each side bsize equ swidth*slen*npgs*2 scrsav dw swidth*slen dup (0700H) ; a blank screen ; circular buffer. To work properly, the buffer size should be an exact ; multiple of swidth*2 cbuf struc pp dw ? ; place ptr in buffer bend dw ? ; end of buffer orig dw ? ; buffer origin lcnt dw 0 ; # of lines in buffer. cbuf ends topbuf db bsize dup (?) botbuf db bsize dup (?) ; top and bottom windows tlbuf db swidth*2 dup (?) blbuf db swidth*2 dup (?) twnd cbuf <topbuf,topbuf+bsize-1,topbuf,0> bwnd cbuf <botbuf,botbuf+bsize-1,botbuf,0> portno db ? prton db 'Printer: on' prtnlen equ $-prton prtoff db 'Printer: off' prtflen equ $-prtoff datas ends code segment public ; code segment extrn prtchr:near,outchr:near,sendbr:near assume cs:code,ds:datas,es:datas scrini proc near ; init screen stuff mov ah,15 ; read video state... int screen mov crt_mode,al ; save crt mode cmp ah,crt_cols ; is real # of cols < passed? jge scrin1 ; no mov crt_cols,ah ; yes, save # of cols scrin1: mov dl,crt_cols ; # of cols again mov dh,crt_lins ; and # of rows dec dl dec dh mov low_rgt,dx ; save away window address mov insmod,0 ; not in insert mode mov dx,cursor ; assume old cursor test flags1,inited ; have we been here before? jnz scrin4 ; yes, use old cursor mov curattr,07 ; else set nice screen attribute mov ttstate,offset outtt0 ; normal screen state mov ah,3 ; figure out where cursor is xor bh,bh ; page 0 int screen ; read cursor position cmp dh,crt_lins ; past logical end of screen? jb scrin2 ; no, keep going mov dh,byte ptr low_rgt+1 ; yes, just use lower right corner scrin2: cmp dl,crt_cols ; maybe past right margin jb scrin3 ; no, use the way it is mov dl,byte ptr low_rgt scrin3: mov cursor,dx ; init cursor scrin4: mov ah,2 xor bh,bh int screen ; set cursor in case it moved ret scrini endp argini proc near ; read passed arguments mov bx,argadr ; base of argument block mov al,[bx].flgs ; get flags and al,capt+emheath+havtt+trnctl+lclecho+modoff mov flags,al ; mask for allowable and save and flags1,not (prtscr) ; these are allowable ; (others remain). mov al,[bx].prt cmp al,portno ; using same port? je argin1 ; yes, go on and flags1,not inited ; else re-init stuff argin1: mov portno,al ; update port number mov al,[bx].cols mov crt_cols,al mov al,[bx].rows mov crt_lins,al ; init # of rows and cols mov ax,[bx].captr mov captrtn,ax ; buffer capture routine mov ax,[bx].belld mov beldiv,ax ; bell divisor mov ax,[bx].klen mov ktlen,ax ; length of key redef tbl mov ax,[bx].ktab mov ktrntab,ax ; save key translation table mov ax,[bx].krpl mov krpltab,ax mov al,[bx].escc mov esc_ch,al ret ; that's it argini endp modlin proc near ; turn on mode line mov al,esc_ch mov modbuf.m_echr,' ' ; first char is initial space mov modbuf.m_hlp,' ' ; goes here too. cmp al,32 ; printable? jnb modl1 ; yes, keep going add al,40h ; made printable mov modbuf.m_echr,'^' ; note control char mov modbuf.m_hlp,'^' modl1: mov modbuf.m_echr+1,al ; fill in character mov modbuf.m_hlp+1,al mov bx,argadr ; get argument block mov al,[bx].baudb ; get baud bits mov si,offset unkbaud ; assume unknown baud cmp al,baudnsiz ; too big? jnb modl2 ; yes, use default mov cl,2 ; each is 4 bytes long shl al,cl mov ah,0 add ax,offset baudn mov si,ax modl2: mov cx,size m_baud ; length of baud space mov di,offset modbuf.m_baud rep movsb ; copy in baud rate mov al,[bx].parity ; get parity code mov cl,2 ; each is 4 bytes long... shl al,cl mov ah,0 add ax,offset parnams ; names of parity settings mov si,ax mov cx,4 ; each is 4 long mov di,offset modbuf.m_par rep movsb mov si,offset lclmsg ; assume remote echoing test flags,lclecho ; echoing? jz modl4 ; no, keep going mov si,offset remmsg modl4: mov cx,3 ; size of on/off mov di,offset modbuf.m_echo rep movsb mov al,'1' cmp portno,1 ; port 1? je modl5 ; yes, keep going mov al,'2' modl5: mov modbuf.m_prt,al ; fill in port number mov cx,size modfrm ; this is size of mode line mov si,offset modbuf ; mode line image ; alternate entry to write an alternate mode line modwrt: push cx push si ; save mode line and size mov dx,24 * 100h ; 25th line for mode line push word ptr curattr ; save current attributes mov curattr,70h ; want inverse video call clreol ; clear to end of line... pop word ptr curattr ; restore attributes mov dx,24 * 100h mov bh,0 mov ah,2 ; set cursor position int screen pop si pop cx ; restore these modl6: lodsb ; get a byte mov ah,14 ; write to terminal mov bh,0 ; page 0 int screen loop modl6 ; write out entire mode line mov dx,cursor mov ah,2 mov bh,0 int screen ; put cursor back where it belongs ret ; and return modlin endp clrmod proc near ; clear mode line mov ax,600h ; blank window mov cx,24 * 100h ; beginning of window mov dx,24 * 100h + 79 ; end of window mov bh,07 ; nice attribute int screen ; clear mode line ret ; and return clrmod endp term proc near ; terminal emulator entry point mov argadr,ax ; save argument ptr push es ; save caller's extra segment address mov ax,seg datas mov es,ax call argini ; init options from arg address call scrini ; init screen stuff test flags1,inited ; have we run yet? jz term1 ; no, forget this part call restscr ; restore screen term1: or flags1,inited ; remember we've run already. call clrmod ; empty mode line test flags,modoff ; is mode line disabled? jnz lp ; yes, skip it call modlin ; turn on mode line lp: call portchr ; char at port? jnc chkinp ; no, keep going call outtty ; print on terminal chkinp: mov ah,1 int kb jz lp ; nothing available... xor ah,ah int kb ; get the char from the buffer push ax ; save character temporarily call gss ; get shift state into al mov bl,al ; save shift state pop ax cmp al,esc_ch ; escape character? je quit ; yes, stop here call trnout ; translate if nec., output to prt jmp chkinp ; and keep going quit: call clrmod ; erase mode line call savescr ; save screen mov al,flags mov bx,argadr mov [bx].flgs,al ; update flags in arg block pop es ; restore segment register ret ; and return to caller term endp ; get shift state into al. We only care about shift, ctl, and alt keys. ; right shift is collapsed into left shift. gss proc near mov ah,2 int kb ; get current shift state mov bl,al ; copy for a moment and bl,right_shift ; mask out all but right shift shl bl,1 ; move right shift to left shift pos or al,bl ; collapse shift bits and al,(left_shift + alt_shift + ctl_shift) ret gss endp ; save the screen so we can restore it ; maybe save cursor also. savescr proc near push ds mov si,0 mov di,offset scrsav ; place to put screen mov cx,80*24 ; # of words on screen call scrseg push ax ; save screen segment call scrwait ; wait for screen to be ready pop ds ; address screen rep movsw ; save the screen pop ds ; restore this ret ; and return savescr endp ; restore screen from scrsav buffer restscr proc near push es mov si,offset scrsav ; source mov di,0 mov cx,80*24 call scrseg mov es,ax call scrwait rep movsw ; restore it pop es ret restscr endp ; send the character in al out to the serial port ; handle echoing also... outprt proc near test flags,lclecho ; echoing? jz outpr1 ; no, forget it push ax ; save char call outtty ; print it pop ax ; restore outpr1: mov ah,al ; this is where outchr expects it call outchr ; output to the port nop nop nop ; skip returns... ret outprt endp ; returns with carry on if a character is available portchr proc near call prtchr ; character at port? jmp short portc1 ; yes, go handle nop ; skip return is stupid... clc ; no carry -> no character ret ; and return... portc1: and al,7fh ; we don't worry about parity here stc ; have a character ret ; and return portchr endp ; translate the scan code in ah according to the translate table ; given in ktrntab/krpltab, output to port. If no translation, ; use ascii char in al. (should probably include shift state ; somewhere). Shift state is in bl. trnout proc near cmp ah,4eh ;*** plus key thing? je trnmod ; yes, go toggle mode line trnou1: test flags,havtt ; translate table given? jz trnou3 ; no, just output character push ax ; save original value mov al,ah ; put scan code into ah mov ah,bl ; shift state into top half. mov di,ktrntab ; pick up translate tbl mov cx,ktlen ; length of tbl repne scasw ; look for our key pop ax ; recover character jne trnou3 ; not found, forget it sub di,ktrntab ; get index into tbl sub di,2 ; (minus 2 for pre-increment) mov bx,krpltab ; get replacement table mov si,[bx][di] ; and addr of replacement mov cl,[si] ; get first byte (length) xor ch,ch ; clear high-order byte inc si ; point to translation string trnou2: lodsb ; get a byte push si push cx ; save important registers call outprt ; send to port pop cx pop si loop trnou2 ; send all chars ret ; and return trnou3: cmp al,0 ; is it a special code? jne trnou4 ; no, don't do this mov al,ah ; get scan code mov cx,lckeys ; length of table mov di,offset ckeys ; table address repne scasb mov al,0 ; ascii code was 0... jne trnou4 ; not found, keep going sub di,offset ckeys+1 ; get table offset shl di,1 ; shift for word offset jmp ckacts[di] ; jump to appropriate routine trnou4: call outprt ; just output single char ret ; and return trnmod: test flags,modoff ; mode line already off? jnz trnm1 ; yes, go turn on call clrmod ; no, clear mode line here or flags,modoff ; turn on flag ret ; and return trnm1: call modlin ; turn on mode line and flags,not modoff ; clear flag ret ; and return trnbrk: mov ah,dconio mov dl,0ffH int dos ; read the bogus ^C DOS gets. call sendbr ret trnprs: xor flags1,prtscr ; flip the flag and flags,not modoff ; turn on mode line mov si,offset prton mov cx,prtnlen test flags1,prtscr ; did it go on? jnz trnpr1 ; yes, say so mov si,offset prtoff mov cx,prtflen trnpr1: call modwrt ; write into mode line ret ; and return ; common entry for arrow keys trnarr: mov cx,2 ; length is always 2 jmp trnou2 ; go send definition trnupw: mov si,offset uptrn jmp trnarr trndnw: mov si,offset dntrn jmp trnarr trnlfw: mov si,offset lftrn jmp trnarr trnrgw: mov si,offset rgtrn jmp trnarr trnout endp ; move viewing window up (screen moves down). ; alternate entry upwin2 doesn't beep if invalid. upwind proc near mov ax,offset tlbuf ; place to put line temporarily mov bx,offset twnd ; where to get lines from call getcirc ; try to get a line jnc upwin3 ; have a line, go show it call outbel ; else ring bel ret ; and return upwin2: mov ax,offset tlbuf mov bx,offset twnd call getcirc jnc upwin3 ret ; this just rets if no line avail. upwin3: mov ax,offset blbuf ; place for bottom line call getbot ; fetch bottom line mov ax,offset blbuf mov bx,offset bwnd call putcirc ; save in circular buffer mov ax,701h ; scroll down one line xor cx,cx ; from top mov dx,low_rgt ; to bottom mov bh,curattr int screen ; scroll it down mov di,0 ; offset for destination mov si,offset tlbuf ; where to get line from mov cx,swidth ; length of line push es call scrseg push ax call scrwait pop es rep movsw ; copy the line in pop es ; restore this ret ; and return upwind endp ; move viewing window down a line (screen scrolls up) ; entry dwin2 does same w/out checking to see if scroll is legal dnwind proc near mov ax,offset blbuf ; place to put line temporarily mov bx,offset bwnd ; where to get lines from call getcirc ; try to get a line jnc dnwin3 ; have a line, go show it call outbel ; else ring bel ret ; and return dnwin2: mov ax,offset blbuf mov bx,offset bwnd call getcirc jnc dnwin3 ret ; this just rets if no line avail. dnwin3: call scrprep ; save top line mov ax,601h ; scroll up one line xor cx,cx ; from top mov dx,low_rgt ; to bottom mov bh,curattr int screen ; scroll it down mov dx,low_rgt mov dl,0 ; get addr of last line call scrloc mov di,ax ; this is offset in dest mov si,offset blbuf ; where to get line from mov cx,swidth ; length of line push es call scrseg push ax call scrwait pop es rep movsw ; copy the line in pop es ; restore this ret ; and return dnwind endp ; move viewing window down as much as possible... endwnd proc near mov cx,1000 ; large number of lines jmp dnwp1 ; and enter dwnpg endwnd endp ; scroll viewing window down (contents move up) crt_lins times... dnwpg proc near mov cl,crt_lins mov ch,0 dnwp1: push cx ; save this call dnwin2 pop cx loop dnwp1 ret ; and return dnwpg endp ; home viewing window homwnd proc near mov cx,1000 ; large # of lines jmp upwp1 ; join upwpg homwnd endp ; scroll viewing window up (screen moves down) a page upwpg proc near mov cl,crt_lins mov ch,0 upwp1: push cx call upwin2 pop cx loop upwp1 ret ; and return upwpg endp ; get the bottom line into the buffer pointed to by ax. getbot proc near push ds mov di,ax ; save dest mov cx,swidth mov dx,low_rgt mov dl,0 call scrloc mov si,ax call scrseg push ax call scrwait pop ds rep movsw pop ds ret getbot endp ; put a line into the circular buffer. Pass the buffer structure ; in bx, the pointer to the line in ax. putcirc proc near push si push di push cx push dx mov di,[bx].pp ; pick up buffer ptr add di,2*swidth ; increment to next avail slot cmp di,[bx].bend ; past end? jb putci1 ; no, leave alone mov di,[bx].orig ; else start at beginning putci1: mov [bx].pp,di ; update ptr mov si,ax ; this is source mov cx,swidth rep movsw ; copy into buffer cmp [bx].lcnt,npgs*slen ; can we increment it? jae putci2 ; no, keep going inc [bx].lcnt ; else count this line putci2: pop dx pop cx pop di pop si ; restore registers ret putcirc endp ; get a line from the circular buffer, removing it from the buffer. ; returns with carry on if the buffer is empty. ; pass the buffer structure in bx, the buffer to copy the line into ; in ax. getcirc proc near push si push di push cx push dx cmp [bx].lcnt,0 ; any lines in buffer? jne getci1 ; yes, ok to take one out. stc ; else set carry jmp short getcir3 ; and return getci1: mov si,[bx].pp ; this is source mov di,ax ; this is dest mov cx,swidth ; # of chars to copy rep movsw mov si,[bx].pp ; get ptr again sub si,2*swidth ; move back cmp si,[bx].orig ; compare to origin jae getcir2 ; still in range, continue mov si,[bx].bend ; else use end of buffer sub si,2*swidth-1 ; minus length of a piece getcir2:mov [bx].pp,si ; update ptr dec [bx].lcnt ; decrement # of lines in buffer clc ; make sure no carry getcir3:pop dx pop cx pop di pop si ret getcirc endp ; call before scrolling to save top line... scrprep proc near push ds mov si,0 ; offset of top line mov cx,swidth ; length of line mov di,offset tlbuf ; place to put line temporarily call scrseg push ax call scrwait pop ds rep movsw ; copy the line pop ds ; restore this mov ax,offset tlbuf mov bx,offset twnd ; this is where it goes call putcirc ; put into buffer ret ; and return scrprep endp ; put the character in al to the screen outtty proc near test flags,capt ; capturing output? jz outnoc ; no, forget this part push ax ; save char call captrtn ; give it captured character pop ax ; restore character and keep going outnoc: test flags1,prtscr ; should we be printing? jz outnop ; no, keep going push ax mov ah,print_out mov dl,al ; put character here for dos... int dos pop ax outnop: test flags,emheath ; emulating heath? jnz outnop1 ; yup, go do something smart mov dl,al mov ah,pbout int dos ; else let dos print char ret ; and return outnop1:mov dx,cursor ; these may need cursor... jmp ttstate ; jump according to current state outtt0: cmp al,32 ; special character? jb outtt1 ; yes, handle specially... cmp insmod,0 ; in insert mode? je outnrm ; no, output normal push ax ; save character call inschr ; insert a character pop ax outnrm: xor bh,bh ; current page mov cx,1 ; only one char mov bl,curattr ; with current attribute mov ah,9 int screen ; put onto screen mov dx,cursor ; get cursor pos currt: inc dl ; bump col cmp dl,crt_cols ; see if in range jb setcur ; in range, go set cursor test flags1,lnwrap ; in wrap mode? jz outign ; no, just return w/out updating cursor xor dl,dl inc dh ; handle wrap setcur: cmp dh,crt_lins jb setc1 ; not off end, keep going push dx ; save row/col call scrprep ; save top line in window buf mov ax,0601h ; scroll up one line xor cx,cx ; from 0,0 mov dx,low_rgt ; to 24,80 mov bh,curattr ; nice attribute int screen ; do the scroll pop dx mov dh,crt_lins ; go to bottom line again... dec dh setc1: xor bh,bh ; page is 0 mov cursor,dx ; save cursor pos mov ah,2 int screen ; set cursor outign: ret ; and return ; special character (in al) outtt1: mov di,offset spctab ; special char table mov cx,lspctab ; length of tbl repne scasb ; look for char in tbl jz outtt2 ; found, go do something with it test flags,trnctl ; are we allowed to print carets? jz outign ; no, just ignore it. push ax ; save char mov al,'^' call outtty ; print caret pop ax add al,'A'-1 ; make printable jmp outtty ; print, then return outtt2: mov dx,cursor ; might need cursor pos sub di,offset spctab+1 ; get index of char shl di,1 ; double for word offset jmp spcjmp[di] ; and go handle ; special char routines. cursor is in dx, char in al outlf: inc dh ; bump row jmp setcur outcr: xor dl,dl ; set col to 0 jmp setcur outbs: or dl,dl jle setcur ; col 0, can't back up dec dl ; back up col jmp setcur ; and use if reasonable outtab: mov dl,byte ptr cursor ; get initial column outta1: mov dh,dl ; save column ptr push dx mov al,' ' ; output a space call outtty ; convenient, huh? pop dx mov dl,byte ptr cursor cmp dh,dl ; is it moving? je outta2 ; no, forget this test dl,7 ; is it a multiple of 8? jnz outta1 ; no, keep going outta2: ret ; else return ; stolen from bios outbel: mov al,10110110b ; timer initialization out timer+3,al mov ax,beldiv ; bel divisor out timer+2,al mov al,ah out timer+2,al ; output divisor in al,bel_prt mov ah,al ; remember original value or al,3 ; turn speaker on out bel_prt,al mov cx,8888h outbe1: loop outbe1 ; wait a while mov al,ah out bel_prt,al ; turn bell off ret ; and return outesc: mov ttstate,offset escseq ; expect escape sequence. ret ; and return ; escape-char handling routines escseq: mov ttstate,offset outtt0 ; put state back to normal mov di,offset esctab ; escape char tbl mov cx,lesctab ; length of tbl repne scasb ; look for it in tbl jz escsq1 ; found, go use it jmp outtty ; not there, just print it escsq1: sub di,offset esctab+1 ; get offset into tbl shl di,1 ; convert to word offset jmp escjmp[di] ; and go dispatch on it ; escape dispatch routines revind: cmp dh,0 jle revin1 dec dh ; back up a row jmp setcur ; and go set cursor revin1: push dx ; save cursor pos mov ax,701h ; scroll down one line xor cx,cx ; from top mov dx,low_rgt ; to bottom mov bh,curattr int screen ; scroll it down pop dx ; restore cursor. mov dh,0 ; set row back to 0 jmp setcur curup: cmp dh,0 ; w/in range? jle curu1 ; no, skip this dec dh ; else back up curu1: jmp setcur ; and go set position curdwn: inc dh jmp setcur ; increment row (setcur can scroll!) ; currt is above clrscr: call curhom ; go home cursor jmp clreow ; then clear to end of window curhom: xor dx,dx ; move to 0,0 jmp setcur clreow: cmp dl,0 ; at beginning of line? jz clrw1 ; yes, skip this part... push dx ; remember cursor pos call clreol ; clear to end of this line pop dx inc dh ; bump row xor dl,dl ; start from col 0 clrw1: cmp dh,crt_lins ; last line on screen jnb clrw2 ; if not in range, forget it mov ax,700h ; clear whole window mov cx,dx ; this is beginning mov dx,low_rgt ; mov dx,174fh ; this is lower right corner mov bh,curattr ; default attribute int screen ; go clear it clrw2: ret ; and return clreol: push es mov cl,crt_cols ; last col + 1 sub cl,dl ; this is # of chars to move xor ch,ch jcxz clrl1 call scrloc ; compute screen location (to ax) mov di,ax call scrseg mov es,ax ; address screen segment call scrwait ; wait for retrace mov ah,curattr ; current attribute mov al,' ' ; fill char rep stosw ; fill line with spaces clrl1: pop es ret ; and return inslin: mov al,1 ; scroll one line ; alternate entry if inserting more then one line inslin1:mov ch,dh ; start at current row xor cl,cl ; column 0 mov dx,low_rgt ; mov dx,174fh ; to bottom of screen mov ah,7h ; scroll down. mov bh,curattr ; attribute int screen ret dellin: mov al,1 ; scroll 1 line ; alternate entry if deleting more than one line dellin1:mov ch,dh ; start at current row xor cl,cl ; column 0 mov dx,low_rgt ; mov dx,174fh ; to bottom of screen mov ah,6h ; scroll up. mov bh,curattr ; attribute int screen ret delchr: push ds push es pushf ; these may get changed... mov cl,crt_cols dec cl sub cl,dl ; from what we're fiddling) xor ch,ch jcxz delch1 ; none to move, forget it call scrloc ; compute location mov di,ax mov si,ax add si,2 ; source is next position over call scrseg ; pick up screen segment push ax ; put screen segment onto stack mov es,ax ; and in destination segment call scrwait ; wait for retrace pop ds ; address screen segment rep movsw ; delete it mov byte ptr [di],' ' ; kill char at end of line delch1: popf pop es pop ds ret inschr: push ds push es ; save these as well pushf ; might as well save flags... mov dx,cursor ; this is place to do it mov cl,crt_cols dec cl ; mov cl,79 ; this is last col to move, +1 for length sub cl,dl ; compute distance to end xor ch,ch ; clear top half of offset jcxz insch1 ; nothing to move... mov dl,crt_cols sub dl,2 ; last col to move ; mov dl,78 ; this is address of last col to move call scrloc ; compute pos mov si,ax mov di,ax add di,2 ; destination is one byte over... std ; remember to move us backwards call scrseg ; find screen segment mov es,ax push ax ; save screen seg on stack call scrwait ; wait until save to write pop ds ; address screen segment rep movsw ; move each char and attribute insch1: popf pop es pop ds ret ; and return noins: mov insmod,0 ; turn off insert mode ret ; and return movcur: mov wcoord,2 ; want two coordinates... mov ttstate,offset getcoord ret ; and return vtident: mov si,offset vtidstr mov cx,lvtidst vtid1: lodsb ; get a byte from the string push si ; have to save from outprt push cx call outprt ; send to serial port pop cx pop si loop vtid1 ; go thru all chars ret ; and return entins: mov insmod,0ffh ; enter insert mode... ret ; and return doansi: mov ansarg,0 ; ansi argument is 0 (default) mov ttstate,offset getaarg ; state is get ansi argument ret getaarg:cmp al,'0' jb getaa1 ; in range for digit? cmp al,'9' ja getaa1 sub al,'0' ; convert to binary mov dl,al ; tuck away mov al,ansarg mov dh,10 mul dh ; shift sum add al,dl ; add in this digit (what about ovfl?) mov ansarg,al ret ; and return getaa1: cmp al,'?' ; the dreaded question mark? jne getaa2 mov ttstate,offset ignn ; we ignore these... mov igncnt,2 ; this is how many chars come after him ret getaa2: mov ttstate,offset outtt0 ; reset state mov dx,cursor ; this needs cursor position mov bl,ansarg xchg al,bl ; put argument in nice place cmp bl,'L' ; insert line? jne getaa3 jmp inslin1 ; and go do it getaa3: cmp bl,'M' ; maybe delete line? jne getaa4 jmp dellin1 getaa4: ret ; ignore. invvid: mov curattr,70h ; attribute for inverse video ret nrmvid: mov curattr,07h ; attribute for normal video ret dowrap: or flags1,lnwrap ; turn on wrap mode ret ; and return nowrap: and flags1,not lnwrap ; turn off wrap mode ret ; and return ; get a coordinate. getcoord: sub al,32 ; coordinates offset by 32 mov si,wcoord dec si mov byte ptr coord[si],al ; fill in appropriate coordinate mov wcoord,si ; update flag jnz getco1 ; more needed, can't do anything yet mov ttstate,offset outtt0 ; reset state mov dx,coord ; get coordinates jmp setcur ; and go jump there getco1: ret ; ignore following igncnt characters ignn: dec igncnt ; decrement count jnz ignn1 mov ttstate,offset outtt0 ; put state back to normal if done ignn1: ret outtty endp ; computes screen location to ax, given row and col in dx. ; trashes ax,bx scrloc proc near mov al,dh ; get row mov bl,crt_cols ;** row size mul bl ; multiply by row size xor dh,dh ; clear col add ax,dx ; this is current position sal ax,1 ; double for attributes ret scrloc endp ; puts current screen segment in ax scrseg proc near mov ax,0b000h ; assume bw for now cmp crt_mode,7 ; 7 is bw (***) je scrse1 mov ax,0b800h ; color card scrse1: ret scrseg endp ; wait for retrace so can write to screen memory scrwait proc near cmp crt_mode,7 ; bw mode? je scrwa3 ; yes, no waiting push dx mov dx,crt_status scrwa1: in al,dx test al,disp_enb ; display enable? jnz scrwa1 ; yes, keep waiting scrwa2: in al,dx test al,disp_enb ; now wait for it to go off jz scrwa2 ; so can have whole cycle pop dx scrwa3: ret ; that was easy... scrwait endp code ends if1 %out [End of pass 1] else %out [End of assembly] endif end //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 640 msyibm.asm /bin/echo -n ' '; /bin/ls -ld msyibm.asm fi /bin/echo 'Extracting msyz100.asm' sed 's/^X//' <<'//go.sysin dd *' >msyz100.asm ; Kermit system dependent module for Heath/Zenith Z100 public term include msdefs.h BIOS_SEG SEGMENT AT 40H ; Define segment where BIOS really is ORG 4*3 BIOS_PRINT LABEL FAR ; Printer output ORG 6*3 BIOS_AUXOUT LABEL FAR ; AUX output routine ORG 26*3 BIOS_AUXFUNC LABEL FAR ; AUX: function ORG 27*3 BIOS_CONFUNC LABEL FAR ; CON: function BIOS_SEG ENDS ; Function codes for BIOS_xxxFUNC CHR_WRITE EQU 0 ; Write character CHR_READ EQU 1 ; Read character CHR_STATUS EQU 2 ; Get status CHR_SFGS EQU 0 ; Get status subfunction CHR_SFGC EQU 1 ; Get config subfunction CHR_CONTROL EQU 3 ; Control function CHR_CFSU EQU 0 ; Set new configuration parameters CHR_CFCI EQU 1 ; Clear input buffer ; Scan code definitions used for translating back to Heath ESC sequences entscan equ 08dh ; enter key scan code f0scan equ 096h ; F0 key f1scan equ 097h ; F1 key f2scan equ 098h ; F2 key f3scan equ 099h ; F3 key f4scan equ 09ah ; F4 key f5scan equ 09bh ; F5 key f6scan equ 09ch ; F6 key f7scan equ 09dh ; F7 key f8scan equ 09eh ; F8 key f9scan equ 09fh ; F9 key f10scn equ 0a0h ; F10 key f11scn equ 0a1h ; F11 key f12scn equ 0a2h ; F12 key homscan equ 0a9h ; Home key upscan equ 0a5h ; Up arrow dnscan equ 0a6h ; Down arrow rtscan equ 0a7h ; Right arrow lfscan equ 0a8h ; Left arrow kpminus equ 0adh ; keypad minus kpdot equ 0aeh ; keypad period kp0 equ 0b0h ; keypad 0 kp1 equ 0b1h ; keypad 1 kp2 equ 0b2h ; keypad 2 kp3 equ 0b3h ; keypad 3 kp4 equ 0b4h ; keypad 4 kp5 equ 0b5h ; keypad 5 kp6 equ 0b6h ; keypad 6 kp7 equ 0b7h ; keypad 7 kp8 equ 0b8h ; keypad 8 kp9 equ 0b9h ; keypad 9 sentscn equ 0cdh ; shifted enter key sf0scan equ 0d6h ; shifted F0 key sf1scan equ 0d7h ; shifted F1 key sf2scan equ 0d8h ; shifted F2 key sf3scan equ 0d9h ; shifted F3 key sf4scan equ 0dah ; shifted F4 key sf5scan equ 0dbh ; shifted F5 key sf6scan equ 0dch ; shifted F6 key sf7scan equ 0ddh ; shifted F7 key sf8scan equ 0deh ; shifted F8 key sf9scan equ 0dfh ; shifted F9 key sf10scn equ 0e0h ; shifted F10 key sf11scn equ 0e1h ; shifted F11 key sf12scn equ 0e2h ; shifted F12 key shomscn equ 0e9h ; shifted Home key supscan equ 0e5h ; shifted Up arrow sdnscan equ 0e6h ; shifted Down arrow srtscan equ 0e7h ; shifted Right arrow slfscan equ 0e8h ; shifted Left arrow skpmins equ 0edh ; shifted keypad minus skpdot equ 0eeh ; shifted keypad period skp0 equ 0f0h ; shifted keypad 0 skp1 equ 0f1h ; shifted keypad 1 skp2 equ 0f2h ; shifted keypad 2 skp3 equ 0f3h ; shifted keypad 3 skp4 equ 0f4h ; shifted keypad 4 skp5 equ 0f5h ; shifted keypad 5 skp6 equ 0f6h ; shifted keypad 6 skp7 equ 0f7h ; shifted keypad 7 skp8 equ 0f8h ; shifted keypad 8 skp9 equ 0f9h ; shifted keypad 9 ; Miscellaneous scan codes used for functions prscan equ f12scn ; print-screen scan code (F12)... brkscan equ 0aah ; Break key modfrm struc ; format of mode line db 'Esc chr: ' m_echr db 2 dup (?) db ', Speed: ' m_baud db 4 dup (?) db ', Parity: ' m_par db 4 dup (?) db ', Echo: ' m_echo db 3 dup (?) db ', Prn: ' m_prs db 3 dup (?) db ', Type ' m_hlp db 2 dup (?) db '? for Help$' modfrm ends datas segment public 'datas' waste db 100h dup(?) ; assembler problem??? flags1 db 0 ; internal flags prtscr equ 80h ; print screen pressed flag ; Key translations - F12 is printscreen ckeys db brkscan,prscan,upscan,dnscan,lfscan,rtscan,homscan db entscan,f0scan,f1scan,f2scan,f3scan,f4scan,f5scan,f6scan db f7scan,f8scan,f9scan,f10scn,f11scn,kpminus db kpdot,kp0,kp1,kp2,kp3,kp4,kp5,kp6,kp7,kp8,kp9 db supscan,sdnscan,slfscan,srtscan,shomscn db sentscn,sf0scan,sf1scan,sf2scan,sf3scan,sf4scan,sf5scan,sf6scan db sf7scan,sf8scan,sf9scan,sf10scn,sf11scn,skpmins db skpdot,skp0,skp1,skp2,skp3,skp4,skp5,skp6,skp7,skp8,skp9 lckeys equ $-ckeys ;ckacts must parallel ckeys above... ckacts dw trnbrk,trnprs,trnupw,trndnw,trnlfw,trnrgw,trnhom,trnkpn dw trnf0,trnf1,trnf2,trnf3,trnf4,trnf5,trnf6,trnf7,trnf8,trnf9 dw trnf10,trnf11,trnkpn,trnkpn,trnkpn,trnkpn,trnkpn dw trnkpn,trnkpn,trnkpn,trnkpn,trnkpn,trnkpn,trnkpn dw trnupw,trndnw,trnlfw,trnrgw,trnhom,trnkps dw trnsf0,trnsf1,trnsf2,trnsf3,trnsf4,trnsf5,trnsf6,trnsf7 dw trnsf8,trnsf9,trnsf10,trnsf11,trnkps,trnkps dw trnkps,trnskp1,trnskp2,trnskp3,trnskp4,trnskp5,trnskp6 dw trnskp7,trnskp8,trnskp9 enascan db ESC,'y?$' ; Enable scan codes disscan db ESC,'x?$' ; Disable scan codes uptrn db esc,'A' dntrn db esc,'B' rgtrn db esc,'C' lftrn db esc,'D' enttrn db cr ; enter key translation homtrn db ESC,'H' ; home key translation dottrn db '.' ; keypad . translation mintrn db '-' ; keypad - translation kp0trn db '0' ; keypad 0 translation kp1trn db '1' ; keypad 1 translation kp2trn db '2' ; keypad 2 translation kp3trn db '3' ; keypad 3 translation kp4trn db '4' ; keypad 4 translation kp5trn db '5' ; keypad 5 translation kp6trn db '6' ; keypad 6 translation kp7trn db '7' ; keypad 7 translation kp8trn db '8' ; keypad 8 translation kp9trn db '9' ; keypad 9 translation senttrn db cr ; shifted enter key translation shomtrn db ESC,'H' ; home key translation sdottrn db '.' ; shifted keypad . translation smintrn db '-' ; shifted keypad - translation skp0trn db '0' ; shifted keypad 0 translation skp1trn db ESC,'L' ; shifted keypad 1 translation skp2trn db ESC,'B' ; shifted keypad 2 translation skp3trn db ESC,'M' ; shifted keypad 3 translation skp4trn db ESC,'D' ; shifted keypad 4 translation skp5trn db ESC,'H' ; shifted keypad 5 translation skp6trn db ESC,'C' ; shifted keypad 6 translation skp7trn db ESC,'@' ; shifted keypad 7 translation skp8trn db ESC,'A' ; shifted keypad 8 translation skp9trn db ESC,'N' ; shifted keypad 9 translation f0trn db ESC,'J' ; F0 translation f1trn db ESC,'S' ; F1 translation f2trn db ESC,'T' ; F2 translation f3trn db ESC,'U' ; F3 translation f4trn db ESC,'V' ; F4 translation f5trn db ESC,'W' ; F5 translation f6trn db ESC,'P' ; F6 translation f7trn db ESC,'Q' ; F7 translation f8trn db ESC,'R' ; F8 translation f9trn db ESC,'0I' ; F9 translation f10trn db ESC,'0J' ; F10 translation f11trn db ESC,'0K' ; F11 translation f12trn db ESC,'0L' ; F12 translation sf0trn db ESC,'E' ; shifted F0 translation sf1trn db ESC,'1A' ; shifted F1 translation sf2trn db ESC,'1B' ; shifted F2 translation sf3trn db ESC,'1C' ; shifted F3 translation sf4trn db ESC,'1D' ; shifted F4 translation sf5trn db ESC,'1E' ; shifted F5 translation sf6trn db ESC,'1F' ; shifted F6 translation sf7trn db ESC,'1G' ; shifted F7 translation sf8trn db ESC,'1H' ; shifted F8 translation sf9trn db ESC,'1I' ; shifted F9 translation sf10trn db ESC,'1J' ; shifted F10 translation sf11trn db ESC,'1K' ; shifted F11 translation sf12trn db ESC,'1L' ; shifted F12 translation ourarg termarg <> modbuf modfrm <> ; mode line buffer ; some static data for mode line unkbaud db 'Unk ' ; must be 4 chars... baudn db '45.5' db ' 50' db ' 75' db ' 110' db ' 135' db ' 150' db ' 300' db ' 600' db '1200' db '1800' db '2000' db '2400' db '4800' db '9600' db ' 19K' db ' 38K' baudnsiz equ 16 ; # of baud rates known (tbl size / 4) parnams db 'Even' db 'Mark' db 'None' db 'Odd ' ; must be 4 chars db 'Spc ' offmsg db 'Off' onmsg db 'On ' lclmsg db 'Lcl' remmsg db 'Rem' datas ends code segment public extrn prtchr:near,outchr:near,putmod:near,clrmod:near,sendbr:near assume cs:code,ds:datas ; This is from the dumb terminal emulator routine in MSXGEN.ASM. ; Had to use bios calls because DOS calls were losing chars. term proc near mov si,ax ; this is source mov di,offset ourarg ; place to store arguments mov ax,ds push es ; save caller's extra segment address mov es,ax ; address destination segment mov cx,size termarg rep movsb ; copy into our arg blk and flags1,not (prtscr) ; print screen mode disabled test ourarg.flgs,emheath ; Are we to use Heath sequences? jnz chkmod ; no, do something else mov ah,prstr mov dx,offset enascan ; enable scan codes int dos chkmod: call clrmod ; clear mode line test ourarg.flgs,modoff ; is mode line disabled? jnz term1 ; yes, skip it call modlin ; turn on mode line term1: call portchr ; char at port? jnc term3 ; no, keep going call outtty ; print on terminal term3: mov ah,chr_status mov al,chr_sfgs ; get number of characters call bios_confunc ; check console cmp bl,0 jz term1 ; no character, go on mov ah,chr_read call bios_confunc ; read it cmp al,ourarg.escc ; escape char? je term4 ; yes, exit push ax ; save char mov ah,al call trnout ; translate if nec., output to port pop ax jmp term1 ; else echo and keep going term4: call clrmod mov ah,prstr mov dx,offset disscan ; disable scan code generation int dos pop es ret term endp ; returns with carry on if a character is available portchr proc near call prtchr ; character at port? jmp short portc1 ; yes, go handle nop ; skip return is stupid clc ; no carry -> no character ret portc1: and al,7fh ; we don't worry about parity here stc ; have a character ret portchr endp ; put the character in al to the screen outtty proc near mov ah,chr_write call bios_confunc test ourarg.flgs,capt ; capturing output? jz outtt1 ; no, forget it call ourarg.captr ; else call the routine outtt1: test flags1,prtscr ; print screen? jz outtt2 ; no, try something else call bios_print outtt2: ret outtty endp ; send the character in al out to the serial port ; handle echoing also... outprt proc near test ourarg.flgs,lclecho ; echoing? jz outpr1 ; no, forget it push ax ; save char call outtty ; print it pop ax ; restore outpr1: mov ah,al ; this is where outchr expects it call outchr ; output to the port nop nop nop ; skip returns... ret outprt endp modlin proc near ; turn on mode line mov al,ourarg.escc mov modbuf.m_echr,' ' ; first char is initial space mov modbuf.m_hlp,' ' ; goes here too. cmp al,32 ; printable? jnb modl1 ; yes, keep going add al,40h ; made printable mov modbuf.m_echr,'^' ; note control char mov modbuf.m_hlp,'^' modl1: mov modbuf.m_echr+1,al ; fill in character mov modbuf.m_hlp+1,al mov al,ourarg.baudb ; get baud bits mov si,offset unkbaud ; assume unknown baud cmp al,baudnsiz ; too big? jnb modl2 ; yes, use default mov cl,2 ; each is 4 bytes long shl al,cl mov ah,0 add ax,offset baudn mov si,ax modl2: mov cx,size m_baud ; length of baud space mov di,offset modbuf.m_baud rep movsb ; copy in baud rate mov al,ourarg.parity ; get parity code mov cl,2 ; each is 4 bytes long... shl al,cl mov ah,0 add ax,offset parnams ; names of parity settings mov si,ax mov cx,4 ; each is 4 long mov di,offset modbuf.m_par rep movsb mov si,offset remmsg ; assume remote echoing test ourarg.flgs,lclecho ; echoing? jz modl4 ; no, keep going mov si,offset lclmsg modl4: mov cx,3 ; size of on/off mov di,offset modbuf.m_echo rep movsb ; mov al,'1' ; cmp portno,1 ; port 1? ; je modl5 ; yes, keep going ; mov al,'2' ;modl5: mov modbuf.m_prt,al ; fill in port number ; mov cx,size modfrm ; this is size of mode line ; mov si,offset modbuf ; mode line image mov si,offset offmsg ; assume printer off test flags1,prtscr ; print screen enabled? jz modl5 ; no, keep going mov si,offset onmsg modl5: mov cx,3 mov di,offset modbuf.m_prs rep movsb mov dx,offset modbuf call putmod ret ; and return modlin endp ; translate the scan code in ah according to the translate table ; given in ktrntab/krpltab, output to port. If no translation, ; use ascii char in al. (should probably include shift state ; somewhere). Shift state is in bl. trnout proc near test ourarg.flgs,havtt ; translate table given? jz trnou3 ; no, just output character cmp ourarg.klen,0 ; did they say we have one je trnou3 ; but we really don't? push ax ; save original value xor ah,ah ; Zero top half mov di,ourarg.ktab mov cx,ourarg.klen repne scasw ; look for our key pop ax ; recover character jne trnou3 ; not found, forget it sub di,ourarg.ktab sub di,2 ; (minus 2 for pre-increment) mov bx,ourarg.krpl mov si,[bx][di] ; and addr of replacement mov cl,[si] ; get first byte (length) xor ch,ch ; clear high-order byte inc si ; point to translation string trnou2: lodsb ; get a byte push si push cx ; save important registers call outprt ; send to port pop cx pop si loop trnou2 ; send all chars ret ; and return trnou3: xor ah,ah ; get scan code mov cx,lckeys ; length of table mov di,offset ckeys ; table address repne scasb ; mov al,0 ; ascii code was 0... jne trnou4 ; not found, keep going sub di,offset ckeys+1 ; get table offset shl di,1 ; shift for word offset jmp ckacts[di] ; jump to appropriate routine trnou4: call outprt ; just output single char ret ; and return ;trnmod: test flags,modoff ; mode line already off? ; jnz trnm1 ; yes, go turn on ; call clrmod ; no, clear mode line here ; or flags,modoff ; turn on flag ; ret ; and return ;trnm1: call modlin ; turn on mode line ; and flags,not modoff ; clear flag ; ret ; and return trnbrk: call sendbr ret trnprs: xor flags1,prtscr ; flip the flag and ourarg.flgs,not modoff ; turn on mode line call modlin ; write into mode line ret ; and return trn1ch: mov cx,1 ; length is always 1 jmp trnou2 ; common entry for arrow keys trn2ch: mov cx,2 ; length is always 2 jmp trnou2 ; go send definition trn3ch: mov cx,3 ; length is always 3 jmp trnou2 trnupw: mov si,offset uptrn jmp trn2ch trndnw: mov si,offset dntrn jmp trn2ch trnlfw: mov si,offset lftrn jmp trn2ch trnrgw: mov si,offset rgtrn jmp trn2ch trnhom: mov si,offset homtrn jmp trn2ch trnent: mov si,offset enttrn jmp trn1ch trnf0: mov si,offset f0trn jmp trn2ch trnf1: mov si,offset f1trn jmp trn2ch trnf2: mov si,offset f2trn jmp trn2ch trnf3: mov si,offset f3trn jmp trn2ch trnf4: mov si,offset f4trn jmp trn2ch trnf5: mov si,offset f5trn jmp trn2ch trnf6: mov si,offset f6trn jmp trn2ch trnf7: mov si,offset f7trn jmp trn2ch trnf8: mov si,offset f8trn jmp trn2ch trnf9: mov si,offset f9trn jmp trn3ch trnf10: mov si,offset f10trn jmp trn3ch trnf11: mov si,offset f11trn jmp trn3ch trnkpn: and al,07fh ; strip high bits jmp trnou4 ; now output it trnkps: and al,03fh ; strip high bits (special case) jmp trnou4 trnsf0: mov si,offset sf0trn jmp trn2ch trnsf1: mov si,offset sf1trn jmp trn3ch trnsf2: mov si,offset sf2trn jmp trn3ch trnsf3: mov si,offset sf3trn jmp trn3ch trnsf4: mov si,offset sf4trn jmp trn3ch trnsf5: mov si,offset sf5trn jmp trn3ch trnsf6: mov si,offset sf6trn jmp trn3ch trnsf7: mov si,offset sf7trn jmp trn3ch trnsf8: mov si,offset sf8trn jmp trn3ch trnsf9: mov si,offset sf9trn jmp trn3ch trnsf10: mov si,offset sf10trn jmp trn3ch trnsf11: mov si,offset sf11trn jmp trn3ch trnskp1: mov si,offset skp1trn jmp trn2ch trnskp2: mov si,offset skp2trn jmp trn2ch trnskp3: mov si,offset skp3trn jmp trn2ch trnskp4: mov si,offset skp4trn jmp trn2ch trnskp5: mov si,offset skp5trn jmp trn2ch trnskp6: mov si,offset skp6trn jmp trn2ch trnskp7: mov si,offset skp7trn jmp trn2ch trnskp8: mov si,offset skp8trn jmp trn2ch trnskp9: mov si,offset skp9trn jmp trn2ch trnout endp code ends end //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 640 msyz100.asm /bin/echo -n ' '; /bin/ls -ld msyz100.asm fi /bin/echo 'Extracting msz100.hlp' sed 's/^X//' <<'//go.sysin dd *' >msz100.hlp KERMIT VERSION 2.26 FOR Z-DOS September 20, 1984 This guide is meant to be a supplement to the MSKERMIT.HLP file. It describes the differences between the Z-100 version of Kermit and the IBM-PC version. This version has not been tested with Z-DOS version 2.0. LOCAL command execution may or may not work. * Program Operation The Z-100 version of Kermit uses a slightly different concept for Heath emulation. The Z-100 normally uses the same escape sequences as the Heathkit H-19 terminal, although a slightly different key arrangement is used. With Heath emulation mode turned on, the normal Z-100/H19 escape sequences will be sent by the function keys and keypad. However, key redefinition will not be available. Printer control is also not available in Heath emulation mode. When Heath emulation is turned off (the default), the function keys and keypad emulate the Heath sequence as best as possible (no checks are made for alternate or shifted keypad modes), but key redefinition is allowed. If a problem is encountered running a program that uses the keypad keys or function keys, swicth to Heath emulation and see if that helps. Heath emulation only affects what characters are sent for keys typed at the keyboard. It does not change the affect of incoming characters or escape sequences. The Z-100 version of Kermit-MS does not support multiple communication ports. The modem is assumed to be connected to the AUX port. This port must be configured as no parity, no handshake, 1 stop bit, 8 data bits, no pad. * Terminal Emulation The Z-DOS version of Kermit-MS uses the following capabilities: Escape Char: ^] Modeline Printer control Key redefinition (with Heath emulation off) Screen scroll is not currently supported. X. Printer Control The contents of a screen may be printed at any time if a printscreen driver has been installed prior to running Kermit. This is usually done by running the PSC command which comes with your ZDOS 1.25 disks. After this, the screen contents may be printed using Shift-F12. If you wish to log a terminal session on the printer and you are not using Heath emulation mode, the F12 function key will start or stop the spooling of incoming characters to the printer. This may also be simulated by the Kermit-MS LOG PRN and CLOSE commands. X. Key Redefinition Key redefinitions are accomplished in the same way as with the IBM-PC version. However, key redefintion may only be used when Heath emulation is turned off. //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 640 msz100.hlp /bin/echo -n ' '; /bin/ls -ld msz100.hlp fi
knutson@ut-ngp.UUCP (Jim Knutson) (10/05/84)
: Run this shell script with "sh" not "csh" PATH=:/bin:/usr/bin:/usr/ucb export PATH all=FALSE if [ $1x = -ax ]; then all=TRUE fi /bin/echo 'Extracting mskermit.doc' sed 's/^X//' <<'//go.sysin dd *' >mskermit.doc 79 9. MS-DOS KERMIT Program: Daphne Tzoar and Jeff Damens, Columbia University; contribu- tions by many others. Language: Microsoft Macro Assembler (MASM) Documentation: Frank da Cruz, Columbia University; Herm Fischer, Litton Data Systems, Van Nuys CA. Version: 2.26 Date: July 1984 Kermit-MS Capabilities At A Glance: Local operation: Yes Remote operation: Yes Transfers text files: Yes Transfers binary files: Yes Wildcard send: Yes ^X/^Y interruption: Yes Filename collision avoidance: Yes Can time out: Yes 8th-bit prefixing: Yes Repeat count prefixing: Yes Alternate block checks: Yes Terminal emulation: Yes Communication settings: Yes Transmit BREAK: Yes IBM mainframe communication: Yes Transaction logging: No Session logging: Yes Raw transmit: No Act as server: Yes Talk to server: Yes Advanced server functions: No Advanced commands for servers: Yes Local file management: Yes Handle file attributes: No Command/init files: Yes Command macros: Yes Kermit-MS is a program that implements the KERMIT file transfer protocol for the IBM PC and several other machines using the same processor family (Intel 8088 or 8086) and operating system family (PC-DOS or MS-DOS, henceforth referred to collectively as MS-DOS, versions 1.1, 2.0, and 2.1, and thereafter). This section will describe the things you should know about the MS-DOS file system in order to make effective use of Kermit, and then it will describe the Kermit-MS program. Version 2 of MS-DOS Kermit runs on a variety of systems, including the IBM PC and XT, the HP-150, the DEC Rainbow 100 and 100+ (MS-DOS 2.05 and above), the Wang PC, and there is a "generic" MS-DOS version. Version 1 was adapted at various stages of development to run on other systems as well, including the Heath/Zenith 100, Tandy 2000, Victor 9000 (Sirius-1), and Seequa Chameleon, and is still available for those sys- tems until support for them and others is added to version 2. 80 The program operates under version 1.1 or 2.0 and above of DOS, although some features require the functionality of 2.0. It runs in ap- proximately 80K of memory -- over and above the memory used by DOS -- which means that your system should have at least 128K of RAM to use version 2 of MS-DOS Kermit; smaller systems may still use Version 1. 9.1. The MS-DOS File System The features of the MS-DOS file system of greatest interest to KERMIT users are the form of the file specifications, and the distinction be- tween pre-MS-DOS 2.0 file names and newer file names which allow direc- tory paths. 9.1.1. File Specifications MS-DOS 2.x file specifications are of the form DEVICE:\PATHNAME\NAME.TYPE where the DEVICE is a single character identifier (for instance, A for the first floppy disk, C for the first fixed disk, D for a RAM disk emulator), PATHNAME is up to 63 characters of identifier(s) (up to 8 characters each) surrounded by reverse slashes, NAME is an identifier of up to 8 characters, and TYPE is an identifier of up to 3 characters in length. Device and pathname may be omitted. The first backslash in the pathname may be omitted if the specified path is relative to the current directory. In the path field, "." means current directory, ".." means parent directory. Some DOS implementations (like Wang) may use slash "/" rather than backslash in the path field. Pathname is normally omitted, and cannot be specified for MS-DOS 1.x or with those commands which allow MS-DOS 1.x use. Device and directory pathnames, when omitted, default to either the user's current disk and directory, or to the current directory search path as specified in the DOS PATH environment variable, depending on the context in which the file name appears. When this manual says that a file is searched for "in the cur- rent path," it means that the PATH is searched first, and if the file is not found, then Kermit-MS looks on the current disk and directory. If the PATH environment variable is empty, Kermit looks only at the current disk and directory. NAME.TYPE is normally sufficient to specify a file, and only this infor- mation is sent along by Kermit-MS with an outgoing file. The device, path, name, and type fields may contain uppercase letters, digits, and the special characters "-" (dash), "_" (underscore), and "$" (dollar sign). (For use only among MS-DOS processors, additional filename special characters allowed are "#&!%'`(){}". DOS 1.x allows others as well.). There are no imbedded or trailing spaces. Other characters may be not be included; there is no mechanism for "quoting" otherwise illegal characters in filenames. The fields of the file specification are set off from one another by the punctuation indicated 81 above. The name field is the primary identifier for the file. The type, also called the extension or suffix, is an indicator which, by convention, tells what kind of file we have. For instance FOO.BAS is the source of a BASIC program named FOO; FOO.OBJ might be the relocatable object module produced by compiling FOO.BAS; FOO.EXE could be an executable program produced by linking FOO.OBJ, and so forth. .EXE and .COM are the normal suffixes for executable programs. The MS-DOS allows a group of files to be specified in a single file specification by including the special "wildcard" characters, "*" and "? ". A "*" matches any string of characters from the current position to the end of the field, including no characters at all; a "?" matches any single character. Here are some examples: *.BAS All files of type BAS (all BASIC source files) in the current directory. FOO.* Files of all types with name FOO. F*.* All files whose names start with F. F?X*.* All files whose names start with F and contain X in the third position, followed by zero or more characters. ?.* All files whose names are exactly one character long. Wildcard notation is used on many computer systems in similar ways, and it is the mechanism most commonly used to instruct Kermit to send a group of files. Note: Kermit-MS uses the "?" character for help while commands are being typed, so the single-character wildcard in Kermit-MS commands is "=" rather than "?". For example Kermit-MS>send =.* would send files of all types whose names were exactly one character long. Kermit-MS users should bear in mind that other (non-MS-DOS) systems may use different wildcard characters. For instance the DEC-20 uses "%" in- stead of "?" as the single character wildcard; when using Kermit-MS to request a wildcard file group from a KERMIT-20 server, the Kermit-MS "=" must be replaced by the DEC-20 "%". 9.1.2. File Formats MS-DOS systems store files as bulk collections of 8 bit bytes, with no particular differences between text, program code, and binary files. ASCII text files consist of lines separated by carriage-return-linefeed sequences (CRLFs), which conforms exactly to the way Kermit represents text files during transmission. Since a non-MS-DOS receiving system might need to make distinctions as to file type, you may need to use 82 various SET functions on the remote system to inform it that the incom- ing file is of some particular (non-default) type, such as binary. In transmitting files between Kermit-MS's, regardless of file contents, the receiving MS-DOS system is equally capable of processing text, code, and data, and in fact has no knowledge of how the bytes in the file are used. MS-DOS (unlike CP/M) is capable of pinpointing the end of file with precision by keeping a byte count in the directory, so one would expect no particular confusion in this regard. However, certain MS-DOS programs continue to use the CP/M convention of terminating a text file with a Control-Z character, and won't operate correctly unless this ter- minating byte is present. Therefore, Kermit-MS users should be aware of a special SET EOF option for both incoming and outbound files, described below. Non-MS-DOS systems may well be confused by nonstandard ASCII files from Kermit-MS. Files produced by Easywriter or Word Star, for example, may need to be converted to conventional ASCII format prior to transmission by commonly available "exporter" programs. Spreadsheet or database files usually need special formatting to be meaningful to non-MS-DOS recipients (though they can be transmitted between MS-DOS systems with Kermit-MS). Furthermore, files created by word processors (such as BLUE or Easy Writer) that store formatting data at the end of the file, after the control-Z and before physical end, will require special processing via SET EOF to strip the formatting data, lest they confuse non-MS-DOS recipients. 9.2. Program Operation Kermit-MS can be run interactively, from a batch file, or as an "external" DOS command. Commands consist of one or more fields, separated by "whitespace" -- one or more spaces or tabs. Upon initial startup, the program executes any commands found in the file MSKERMIT.INI in the current path. This initialization file may contain command macro definitions, communications settings for one or more ports, or any other Kermit-MS commands. Here is a sample MSKERMIT.INI file: set warning on ; Enable filename collision avoidance. ; ; Define some macros ; define unix set local-echo off, set flow xon, set timer off def ibm set parity odd, set local on, set handsh xon, set timer on def modem set port 2, set baud 1200 def noisy set block-check 3, set send packet-length 40 ; ; Select a port ; set port 1 ; Select COM1 for communications, set baud 4800 ; setting the speed to 4800 baud, connect ; and make a terminal connection. 83 Note that comments may be included by prefixing them with a semicolon. The program can be run in several ways. Interactive Operation: To run Kermit-MS interactively, invoke the program from DOS command level by typing its name. When you see the command's prompt, Kermit-MS> you may type Kermit commands repeatedly until you are ready to exit the program, for example: A> A>kermit IBM PC Kermit-MS V2.26 Type ? for help Kermit-MS>send foo.* informational messages about the files being sent Kermit-MS>get bar.* informational messages about the files being received Kermit-MS>exit A> During interactive operation, you may edit the command you're currently typing to erase the character most recently typed (BACKSPACE or DEL), the most recent field (CTRL-W), or the entire command (CTRL-U). In ad- dition, you may use the help ("?") and recognition (ESC) features freely while typing Kermit-MS commands. A question mark typed at almost any point in a command produces a brief description of what is expected or possible at that point; for this reason, Kermit-MS uses "=" for the single-character match wildcard in local filenames. ESC typed at any point, even in a local filename, will cause the current field to be filled out if what you have typed so far is sufficient to identify it, and will leave you in position to type the next field (or to type a "?" to find out what the next field is); otherwise, the program will beep at you and wait for you to type further characters. Some Kermit-MS commands, like GET, SHOW KEY, SET KEY, may prompt for ad- ditional information on subsequent lines. If you have reached one of these prompts and then wish to cancel the command, you may type Control-C. Summary of Kermit-MS Command Characters: BACKSPACE Delete the character most recently typed. May be typed repeatedly to delete backwards. You may also use DELETE, RUBOUT, or equivalent keys. 84 CTRL-W Delete the most recent "word", or field, on the command line. May be typed repeatedly. CTRL-U Delete the entire command line. CTRL-C Cancel the current command and return to the "Kermit-MS>" prompt. ? Type a brief message describing what you are expected to type in the current field. ESC If enough characters have been supplied in the current field (keyword or file name) to uniquely identify it, supply the remainder of the field and position to the next field of the command. Otherwise, sound a beep. = Wildcard character for matching single characters in filenames, equivalent to MS-DOS "?". Command Line Invocation: Kermit-MS may also be invoked with command line arguments from DOS com- mand level, for instance: A>kermit send foo.bar or A>kermit set port 1, set baud 9600, connect In this case, help and recognition are not available (because the program won't start running until after you type the entire command line), and Kermit-MS will exit after completing the specified command or commands. Therefore, when invoked with command line arguments, Kermit-MS will behave as if it were an external DOS command, like MODE. Note that several commands may be given on the command line, separated by commas. Batch Operation: Like other MS-DOS programs, Kermit-MS may be operated under batch with either command line arguments and/or TAKE files; Kermit will also run interactively if invoked from batch, but it will read commands from the keyboard and not the batch file. 9.3. Kermit-MS Commands MS-DOS Kermit implements a large subset of the commands of "ideal" Ker- mit. Here's a brief summary: BYE to remote server. CLOSE log file and stop logging remote session. CONNECT as terminal to remote system. 85 DEFINE macros of Kermit-MS commands. DELETE local files. DIRECTORY listing of local files. DO a macro expansion. EXIT from Kermit-MS. FINISH Shut down remote server. GET remote files from server. HELP about Kermit-MS. LOCAL prefix for local file management commands. LOG remote terminal session. LOGOUT remote server. PUSH to MS-DOS command level. QUIT from Kermit-MS RECEIVE files from remote Kermit. REMOTE prefix for remote file management commands. RUN an MS-DOS program. SEND files to remote Kermit. SERVER mode of remote operation. SET various parameters. SHOW various parameters. SPACE inquiry. STATUS inquiry. TAKE commands from file. The remainder of this section concentrates on the commands that have special form or meaning for MS-DOS Kermit. Not all of the following commands are necessarily available on all MS-DOS systems, and some of the commands may work somewhat differently between DOS versions. 9.3.1. Commands for File Transfer The file transfer commands are SEND, GET, and RECEIVE. THE SEND COMMAND Syntax: SEND filespec1 [filespec2] The SEND command causes a file or file group to be sent from the local MS-DOS system to the Kermit on the remote system. The remote Kermit may be running in either server or interactive mode; in the latter case, you should already have given it a RECEIVE command and escaped back to your PC. filespec1 may contain a device designator, like "A:" and the wildcard characters "*" and/or "=". The current release of Kermit-MS, however, does not allow pathnames in the SEND command file specification. If filespec1 contains wildcard characters then all matching files will be sent, in the same order that MS-DOS would show them in a directory listing. If filespec1 specifies a single file, you may direct Kermit-MS to send that file with a different name, given in filespec2. For in- stance, in the command Kermit-MS>send foo.bar framus.widget 86 filespec2 begins with the first nonblank character after filespec1 and ends with the carriage return; thus it may contain blanks or other un- usual characters that may be appropriate on the target machine. Lower case letters in filespec2 are raised to upper case for transmission. If a file can't be opened for read access, standard MS-DOS recovery procedures will take place. For example: Not ready error reading drive A Abort, Retry, Ignore? If you select "Abort," you will be returned to DOS. Files will be sent with their MS-DOS filename and filetype (for instance FOO.TXT, no device or pathname). Each file is sent as is, with no con- versions done on the data, except for possibly adding or deleting a ter- minating Control-Z character (see the SET EOF command). Once you give Kermit-MS the SEND command, the name of each file will be displayed on your screen as the transfer begins; packet, retry, and other counts will be displayed along with informational messages during the transfer. If the file is successfully transferred, you will see "Complete", otherwise there will be an error message. When the specified operation is done, the program will sound a beep. Several single-character commands may be given while a file transfer is in progress: ^X (Control-X) Stop sending the current file and go on to the next one, if any. ^Z Stop sending this file, and don't send any further files. ^C Return to Kermit-MS command level immediately without sending any kind of notification to the remote system. ^E Like ^C, but send an Error packet to the remote Kermit in an at- tempt to bring it back to server or interactive command level. CR Simulate a timeout: resend the current packet, or NAK the expected one. Control-X and Control-Z send the proper protocol messages to the remote Kermit to bring it gracefully to the desired state. Control-C leaves the remote Kermit in whatever state it happens to be in. Control-E "aborts" any protocol that is taking place. THE RECEIVE COMMAND Syntax: RECEIVE [filespec] The RECEIVE command tells Kermit-MS to receive a file or file group from the other system. Kermit-MS simply waits for the file to arrive; this command is not to be used when talking to a Kermit server (use GET for that). You should already have issued a SEND command to the remote Ker- 87 mit and escaped back to Kermit-MS before issuing the RECEIVE command. If the optional filespec is provided, store the incoming file under that name. The filespec may include a device designator, or may consist of only a device designator. The incoming file is stored on the default or specified device (current directory in DOS 2.0 and thereafter). If no name was specified, the name from the incoming file header packet is used; if that name is not a legal MS-DOS file name, Kermit-MS will delete excessive characters from it, and will change illegal characters to the letter X. If the optional filespec was provided, but more than one file arrives, the first file will be stored under the given filespec, and the remainder will be stored under their own names, but on the specified device. If an incoming file does not arrive in its entirety, Kermit-MS will nor- mally discard it; it will not appear in your directory. You may change this behavior by using the command SET INCOMPLETE KEEP, which will cause as much of the file as arrived to be saved in your directory. The same single-character commands are available as during SEND: ^X Request that the remote Kermit stop sending the current file, and proceed to the next one immediately. Since this is an optional feature of the Kermit protocol, the remote Kermit might not honor the request. ^Z Request that the remote Kermit terminate the entire transfer; this is also an optional feature that may or may not be supported by the remote Kermit. ^C, ^E, and CR operate in the same way as they do during SEND. If the incoming file has the same name as a file that already exists, and WARNING is set ON, Kermit-MS will change the incoming name (and in- form you how it renamed it) so as not to obliterate the pre-existing file. If WARNING is OFF, the original file will be overwritten; if you type ^X or ^Z to interrupt the transfer, you'll either get a partial new file, or else both the old and the new file of that name will be lost, depending on SET INCOMPLETE. In any case, when WARNING is off, files with the same name as incoming files will not survive. Caution: If an incoming file's name (the part before the dot) cor- responds to an MS-DOS device name, such as NUL, COM1, CON, AUX, or PRN, output will go to that device, rather than to a file with that name. This is a feature of MS-DOS. 88 THE GET COMMAND Syntax: GET remote-filespec The GET command requests a remote KERMIT server to send the file or file group specified by remote-filespec. This command can be used only when Kermit-MS has a KERMIT server on the other end of the connection. This means that you must have CONNECTed to the other system, logged in, run KERMIT there, issued the SERVER command, and escaped back (e.g. ^]C) to the local Kermit-MS. If the remote Kermit does not have a SERVER com- mand, then you should use SEND and RECEIVE as described above. You may use the GET command to specify a different name for storing the incoming. Just type GET alone on a line, and you will be prompted separately for the remote filespec and the local filespec: Kermit-MS>get Remote Source File: com1.txt Local Destination File: xcom1.txt If more than one file arrives, only the first will be renamed. The remote filespec is any string that can be a legal file specification for the remote system; it is not parsed or validated locally. It can contain whatever wildcard or file-group notation is valid on the remote system. As files arrive, their names will be displayed on your screen, along with packet traffic statistics and status messages. You may type ^X to request that the current incoming file be cancelled, ^Z to request that the entire incoming batch be cancelled, and ^C or ^E to return im- mediately to the Kermit-MS> prompt, exactly as described for the RECEIVE command. 9.3.2. Commands for Connecting and Disconnecting The CONNECT command connects your PC as a terminal to the remote system, so that you can start up Kermit there. The BYE, FINISH, and LOGOUT com- mands allow you to shut down a remote Kermit server. BYE When communicating with a remote KERMIT server, use the BYE command to shut down the server, log out its job, and exit from Kermit-MS to DOS. FINISH Like BYE, FINISH shuts down the remote server. However, FINISH does not log out the server's job. You are left at Kermit-MS prompt level so that you can connect back to the job on the remote system. LOGOUT The LOGOUT command is identical to the BYE command, ex- cept you will remain at Kermit-MS prompt level, rather than exit to DOS, so that you can establish another con- nection. 89 The CONNECT Command Establish an interactive terminal connection to the system connected to the currently selected communications port (e.g. COM1 or COM2) using full duplex (remote) echoing and no parity unless otherwise specified in previous SET commands. Get back to Kermit-MS by typing the escape character followed by the letter C. The escape character is Control-] by default. You can use the SET ESCAPE command to define a different escape charac- ter, and on some systems (including the PC and XT) you can SET BAUD to change the baud rate, and SET PORT to switch between ports. Terminal emulation is described in greater detail in section 9.4 below. 9.3.3. Commands for File Management Kermit-MS provides commands or managing both local and remote files. THE REMOTE COMMANDS The REMOTE keyword is a prefix for a number of commands. It indicates that the command is to be performed by the remote Kermit, which must be running as a server. Note that not all Kermit servers are capable of executing all these commands, and some Kermit servers may be able to perform functions for which Kermit-MS does not yet have the correspond- ing commands. In case you send a command the server cannot execute, it will send back a message stating that the command is unknown to it. If the remote server can execute the command, it will send the results to your screen. Here are the REMOTE commands which Kermit-MS may issue: CWD [directory] Change Working Directory on the remote host. Change the default source and destination area for file transfer and management. You will be prompted for a password, which will be erased as you type it. If you do not supply a password (i.e. you type only a carriage return), the server will attempt to access the specified directory without a password. If you do not supply a directory name, your default or login directory on the remote system will be assumed. DELETE filespec Delete the specified file or files on the remote system. In response, the remote host should display a list of the files that were or were not successfully deleted. DIRECTORY [filespec] The remote system will provide a directory listing of the specified files. If no files are specified, then all files in the default area (the current working directory) will be listed. HELP The remote host tells what server functions it is capable of. HOST [command] Send the command to the remote system's command proces- 90 sor for execution. SPACE [directory] Provide a brief summary of disk usage in the specified area on the remote host. If none specified, the default or current area will be summarized. TYPE filespec Display the contents of the specified remote file or files on the screen. THE LOCAL COMMAND The LOCAL keyword is a prefix for a number of commands. It indicates that the specified command is to be executed on the local MS-DOS system. The LOCAL prefix may be omitted. The local commands available are: DELETE filespec Deletes the specified file or files. As in DOS, the names of the deleted files are not listed, only the mes- sage "file(s) deleted" or "file(s) not found", and if you give the command "delete *.*", Kermit-MS will prompt "Are you sure?", like DOS. DIRECTORY [filespec] Lists the names, sizes, and creation dates of files that match the given file specification. If no filespec is given, the command is equivalent to DIR *.*. SPACE Performs the MS-DOS CHKDSK function by running the CHKDSK program from the current path, or default disk under DOS 1.1. RUN filespec Runs the specified file, which must be in .EXE or .COM format, from the specified path or according to the value of the PATH variable if no path was included in the filespec. This command requires MS-DOS 2.0 or higher. PUSH Invokes an MS-DOS command processor "under" Kermit-MS, either COMMAND.COM or whatever shell you have specified with COMSPEC. When you return to Kermit-MS (for in- stance, by typing the MS-DOS EXIT command), you will find Kermit-MS as you left it, with all settings intact. This command only works in MS-DOS 2.0 or higher. The local RUN command has various uses, one of which is to supplement the features of Kermit-MS. For instance, suppose there is an involved procedure that you regularly perform on a certain remote system -- this might include giving commands to a modem to dial the system, looking for a particular herald or prompt, performing a login command sequence, run- ning a selected application, and then running Kermit to send the results back to your PC. You could write a program in the compiled language of your choice, say C or BASIC, to send the desired commands to your modem and the remote system and to look for the appropriate responses. You could put all this in a Kermit-MS TAKE command file (see below), like 91 run update.com receive The program, called UPDATE in this case, does everything up to and in- cluding starting Kermit sending from the remote system. When the program terminates, the next Kermit-MS command, "receive," is executed from the command file. When the end of the command file is reached, in- teractive operation is resumed. THE TAKE COMMAND Syntax: TAKE filespec Execute Kermit commands from the specified file, which may include an explicit path; if no path is specified, the value of the PATH variable is used; if PATH has no value, then the current disk and directory are searched. The command file may include TAKE commands, but it cannot in- clude characters to be sent to a remote host during terminal emulation (i.e. after a CONNECT command). A command file may include comments prefixed by semicolons. THE LOG COMMAND Syntax: LOG filespec Specifies that all characters that appear on your screen during CONNECT will be recorded in the specified file. This allows you to "capture" files from a remote system that doesn't have Kermit, as well as to record remote command typescripts. The log is closed when you EXIT from Kermit-MS or when you issue an explicit CLOSE command. 9.3.4. The SERVER Command Kermit-MS is capable of acting as a Kermit server, providing file trans- fer for users coming in through one of the communication ports. The current version of Kermit-MS can send files (the user on the other end types the GET command), receive files (the user types SEND), and ter- minate, giving control back to the console (user types BYE). To put Kermit-MS into server mode, first issue any desired SET commands to select and configure the desired port, and then type the SERVER com- mand. Kermit-MS will await all further instructions from the user Ker- mit on the other end of the connection, which may be hardwired or con- nected through an autoanswer modem. For example: Kermit-MS>set port 1 Kermit-MS>set baud 1200 Kermit-MS>set timer on Kermit-MS>set warning on Kermit-MS>server 92 9.3.5. The SET Command Syntax: SET parameter [value] Establish or modify various parameters for file transfer or terminal connection. You can examine their values with the SHOW command. Note that there is no "set ibm" command; IBM mainframe communication parameters may be selected with a command macro (see below). The fol- lowing SET commands are available in Kermit-MS: BAUD Communications port line speed BELL Whether to beep at the end of a transaction BLOCK-CHECK-TYPE Level of error checking for file transfer DEBUG Display packet contents during file transfer DEFAULT-DISK Default disk drive for file i/o DESTINATION Default destination device for incoming files END-OF-LINE Packet terminator EOF Method for determining or marking end of file ESCAPE Escape character for CONNECT FLOW-CONTROL Enable or disable XON/XOFF HANDSHAKE Half-duplex line turnaround option HEATH19 Heath/Zenith-19 terminal emulation INCOMPLETE What to do with an incompletely received file KEY Specify key redefinitions, or "keystroke macros" LOCAL-ECHO Specify which host does the echoing during CONNECT PARITY Character parity to use PORT Select a communications port PROMPT Change the "Kermit-MS>" prompt to something else RECEIVE Request remote Kermit to use specified parameters REMOTE For running Kermit-MS interactively from back port SEND Use the specified parameters during file transfer TAKE-ECHO Control echoing of commands from TAKE files TIMER Enable/disable timeouts during file transfer WARNING Specify how to handle filename collisions The SET commands that are peculiar to MS-DOS Kermit are now described in greater detail. The others behave as in "ideal" Kermit. SET BAUD Syntax: SET BAUD rate Set the speed of the currently selected terminal communications port (COM1 by default) to 300, 1200, 1800, 2400, 4800, 9600 or other common baud rate. Some implementations do not support this command. In any case, Kermit-MS leaves the current communication port settings alone un- less you issue explicit SET commands to change them. SET BELL Syntax: SET BELL ON or OFF Specifies whether bell (beeper) should sound upon completion of a file transfer operation. 93 SET DEBUG Syntax: SET DEBUG ON or OFF ON Display the Kermit packet traffic on your screen during file transfer. If the debugger is loaded, transfer control to it when CTRL-C is typed. In Heath-19 terminal emulation on the IBM PC, display unusual control characters in uparrow notation. OFF Don't display debugging information (this is the default). If debugging was in effect, turn it off. SET DEFAULT-DISK Syntax: SET DEFAULT-DISK x: Specify the default disk drive to use for file transfer, directory list- ings, and so forth. Equivalent to typing the DOS command for changing disks. SET DESTINATION Syntax: SET DESTINATION device Specify the device for incoming files, DISK or PRINTER. SET DESTINATION PRINTER will cause incoming files to be spooled directly to the printer. The normal destination is DISK. END-OF-LINE Syntax: SET END-OF-LINE number If the remote system needs packets to be terminated by anything other than carriage return, specify the decimal value of the desired ASCII character. SET EOF Syntax: SET EOF option Controls how the end of file is handled. The options are: CTRL-Z Append a Control-Z character to the end of an incoming file, unless it already ends with a Control-Z. Certain MS-DOS text editors and other applications require files to be in this format. For outbound files, treat the first Control-Z as the end of file, and do not send it nor any characters following it. NOCTRL-Z (Default) Store incoming files exactly as is, and send MS DOS files exactly as is (according to their byte count). 94 SET ESCAPE Syntax: SET ESCAPE character Specify the control character you want to use to "escape" from remote connections back to Kermit-MS. The default is normally ^] (Control- Rightbracket). The character is entered literally, and should normally be chosen from the ASCII control range. SET FLOW-CONTROL Syntax: SET FLOW-CONTROL option Specify the full duplex flow control to be done on the currently selected port. The current options are XON/XOFF and NONE. The specified type of flow control will be done during both terminal emula- tion and file transfer. If set to XON/XOFF, HANDSHAKE is automatically set to OFF. SET HANDSHAKE Syntax: SET HANDSHAKE option Specify any half-duplex handshaking to be done on the currently selected port. The options are BELL, CR, LF, NONE, XOFF, or XON. The specified handshaking will be done during file transfer only. If HANDSHAKE is set to anything other than NONE, FLOW-CONTROL is automatically set to OFF. SET HEATH19 Syntax: SET HEATH19 ON or OFF Specify whether Kermit-MS should use its built-in software facility for emulating a Heath/Zenith-19 (H19) terminal. ON During CONNECT, incoming characters are to be examined for H19 terminal screen control commands (escape sequences), and if en- countered, the commands are to be emulated on the PC screen. The H19 codes are a superset of the popular DEC VT52 codes, so if your system does not support the Heath-19, you may tell it that your terminal type is VT52 (or one of the many VT52 compatibles). The Heath-19 codes are listed in section 9.10, below. OFF All incoming characters will be sent to the screen "bare", through DOS. If you have loaded a device driver into DOS for the CON: device, such as ANSI.SYS, then that driver will be able to interpret the codes itself. Most non-IBM systems have their own screen control code interpreter built into DOS or firmware, or available as a loadable device driver. See section 9.4 for details about terminal emulation. 95 SET KEY Syntax: SET KEY key-specifier Specifies that when the designated key is struck during terminal emula- tion, the associated character string is sent. The key-specifier is one of the keywords F1, F2, ..., or SCAN followed by a scan code. Systems that have a BACKSPACE key also include BACKSPACE as a keyword. If SCAN is used, it is followed by a decimal number to indicate the scan code of the key, which you would ascertain from your system reference manual, or else by using the Kermit-MS SHOW KEY command. SET KEY prompts you on a new line for the definition string. Certain charac- ters, like ESC and CR, may not be entered literally into the string, but can be included by inserting escape codes of the form \ooo, a backslash followed by a 2- or 3-digit octal number corresponding to the ASCII value of the desired character. If some other key redefinition package, like ProKey, has been loaded, then its redefinitions will take precedence over Kermit's. The SET KEY command is illustrated in the terminal emulation section, 9.4, below. SET LOCAL-ECHO Syntax: SET LOCAL-ECHO option Specify how characters are echoed during terminal emulation on the cur- rently selected port. ON specifies that characters are to be echoed by Kermit-MS (because neither the remote computer nor the communications circuitry has been requested to echo), and is appropriate for half- duplex connections. LOCAL-ECHO is OFF by default, for full-duplex, remote echo operation. When you SET LOCAL-ECHO ON, the current HANDSHAKE (if any) is automati- cally enabled and full-duplex FLOW-CONTROL is automatically turned off. When you SET LOCAL-ECHO OFF, HANDSHAKE is also disabled, and the current mode of FLOW-CONTROL (if any) is enabled. If this behavior is un- desired, you may override it by typing explicit SET HANDSHAKE or SET FLOW commands after entering the SET LOCAL-ECHO command. SET PARITY Syntax: SET PARITY keyword Specify the character parity to be used on the currently selected port. The choices for SET PARITY are NONE (the default), ODD, EVEN, MARK, and SPACE. NONE means no parity processing is done, and the 8th bit of each character can be used for data when transmitting binary files. You will need to SET PARITY to ODD, EVEN, MARK, or possibly SPACE when communicating with a system, or over a network, or through modems, con- centrators, multiplexers, or front ends that require or impose character parity on the communication line. For instance, GTE Telenet requires 96 MARK parity. If you neglect to SET PARITY when the communications equi- pment requires it, the symptom may be that terminal emulation works par- tially, and file transfer does not work at all. If you have set parity to ODD, EVEN, MARK, or SPACE, then Kermit-MS will request that binary files will be transferred using 8th-bit-prefixing. If the other side knows how to do 8th-bit-prefixing (this is an optional feature of the KERMIT protocol, and not all implementations of KERMIT have it), then binary files can be transmitted successfully. If NONE is specified, 8th-bit-prefixing will not be requested. Note that there is no advantage to using parity; it only slows Kermit file transfer down. The SET PARITY command is provided only to allow Kermit to adapt to hardware that insists upon using parity. SET PORT Syntax: SET PORT number On machines with more than one communications port, select the port to use for file transfer and CONNECT. This command lets you use a dif- ferent asynchronous adapter, or switch between two or more simultaneous remote sessions. Subsequent SET BAUD, PARITY, HANDSHAKE, FLOW, and LOCAL-ECHO commands will apply to this port only. SET PORT 1 selects COM1, SET PORT 2 selects COM2. SET REMOTE Syntax: SET REMOTE ON or OFF If you wish to run Kermit-MS interactively through the back port, for instance after the operator has done CTTY COM1, you must give the com- mand SET REMOTE ON; this suppresses the file transfer display screen, so that the display won't interfere with the file transfer itself. SET RECEIVE Syntax: SET RECEIVE parameter value At the beginning of a protocol operation, request the remote Kermit to use the given value specified parameter, or inform Kermit-MS that the remote Kermit will be using it. PACKET-LENGTH Ask the remote Kermit to use the specified maximum length for packets that it sends to Kermit-MS. The nor- mal (and maximum) length is 94. Use this command to shorten packets if the communication line is noisy; this will decrease the probability that a particular packet will be corrupted, and will reduce the retransmission overhead when corruption occurs, but it will increase the protocol overhead. PADCHAR Ask the remote Kermit to use the given character for in- terpacket padding. Kermit-MS should never require any 97 padding. PADDING Ask the remote Kermit to insert the given number of pad- ding characters before each packet it sends. This should never be necessary. START-OF-PACKET The remote Kermit will be marking the beginning of pack- ets with something other than Control-A. This will be necessary only if the hosts or communication equipment involved cannot pass a Control-A through as data. TIMEOUT Ask the remote Kermit to time out after the given number of seconds if a packet expected from Kermit-MS has not arrived. Use this command to change the normal timeout interval. SET SEND Syntax: SET SEND parameter value PACKET-LENGTH Use the specified maximum length for outbound packets. Normally, Kermit-MS uses whatever length the other Ker- mit requests. PADCHAR Use the specified character for interpacket padding. Some hosts may require some padding characters (normally NUL or DEL) before a packet. PADDING How many padding characters to use between packets, nor- mally zero. QUOTE Use the indicated printable character for prefixing (quoting) control characters and other prefix charac- ters. The only reason to change this would be for send- ing a very long file that contains very many "#" charac- ters (the normal control prefix) as data. START-OF-PACKET Mark the beginning of outbound packets with some control character other than Control-A. This will be necessary only if the remote host or the communication channel in- volved cannot accept a Control-A as data. The remote host must have been given the corresponding SET RECEIVE START-OF-PACKET command. TIMEOUT Change Kermit-MS's normal timeout interval; this command is effective only if TIMER is set to be ON; it is nor- mally OFF so that the remote KERMIT can control timeouts. 98 SET TAKE-ECHO Syntax: SET TAKE-ECHO ON or OFF Specifies whether screen display should occur during implicit or ex- plicit TAKE operations on MSKERMIT.INI or other Kermit-MS command files, and during evaluation of macro definitions. Handy for finding errors in command files. SET TIMER Syntax: SET TIMER ON or OFF Enable or disable the timer that is used during file transfer to break the deadlock that occurs when an expected packet does not arrive. By default, the timer is OFF, because Kermit-MS is usually used in conjunc- tion with a mainframe that is doing its own timeouts. During a file transfer, it is sufficient for one side to do the timing out and the mainframe is usually better equipped to adjust timeout intervals based on system load or other conditions. The timer should be set ON if you are communicating with a system that cannot do timeouts, such as IBM VM/CMS Kermit. SET WARNING Syntax: SET WARNING option Specify what to do when an incoming file has the same name as an exist- ing file in the default directory of the default device. If ON, Kermit will warn you when an incoming file has the same name as an existing file, and automatically rename the incoming file (as indicated in the warning message) so as not to destroy (overwrite) the pre-existing one. If OFF, the pre-existing file is destroyed, even if the incoming file does not arrive completely. 9.3.6. The SHOW Command Syntax: SHOW option Currently, most parameters that may be altered with SET commands are displayed by the STATUS command. The SHOW command is used for display- ing macro definitions and key redefinitions. The SHOW MACROS command displays the definitions of all currently defined macros. The SHOW KEY command allows you to determine the scan code produced by pressing a given key, so that you can construct a SET KEY command to redefine the key. If the key already has a redefinition in effect, that too will be displayed. In this example, a DEC Rainbow user determines the scan code for the accent grave key, and then redefines that key to send ESC: 99 Kermit-MS>show key Press a key: ` Scan Code: 96 Definition: Kermit-MS>set key scan 96 Definition string: \33 Kermit-MS>show key Press a key: ` Scan Code: 96 Definition: \33 Kermit-MS> The SHOW KEY command only works on certain systems. 9.3.7. Command Macros Kermit-MS provides a facility for combining commands into "macros." Command macro definitions may be included in your MSKERMIT.INI file, TAKEn explicitly from a specified file, or typed interactively, and may be invoked with the DO command. THE DEFINE COMMAND Kermit-MS command macros are constructed with the DEFINE command. The syntax is DEFINE macro-name [command [, command [, ...]]] Any Kermit-MS commands may be included. Example: define telenet set parity mark, set baud 1200, connect THE DO COMMAND A Kermit-MS command macro is invoked using the DO command. For in- stance, Kermit-MS comes with a predefined macro to allow convenient setup for IBM communications; to invoke it, you would type do ibm The IBM macro is defined as "parity mark, handshake xon, local-echo on, timer on". You can delete or replace this definition by adding a new (perhaps null) definition, such as define ibm parity even, handshake cr, local-echo on, timer on or define ibm 100 9.4. Terminal Emulation When you issue the CONNECT command, your PC acts as a terminal connected to a remote computer through the currently selected port. The charac- ters you type are sent out the port, and characters that arrive at the port are displayed on your screen. If you have not previously issued a SET PORT command, COM1 is used. If you have SET LOCAL-ECHO ON for the selected port, then Kermit-MS will display characters on the screen as you type them. If LOCAL-ECHO is OFF, then XON/XOFF flow control will be done unless you have SET FLOW-CONTROL OFF. If you have SET PARITY to anything other than NONE, Kermit-MS will add the appropriate parity to each outbound character, and strip any parity from incoming ones. While CONNECTed, you can also communicate with an autodialer or "smart modem" to control the communications line, hang it up, and the like; for in- stance, typing +++ to a Hayes-like modem will allow you to follow that by dialing or hangup commands. If Heath-19 emulation is being done, incoming characters will be monitored for H19/VT52 escape sequences. These will be interpreted ac- cording to the table in section 9.10. In addition, keys on the numeric keypad will send H19/VT52 sequences unless you disable this feature in some way, for instance by pressing Num Lock on the IBM PC keyboard, or with key redefinitions. Caution: On some systems, such as the IBM PC and XT, Kermit-MS accesses the screen memory memory directly to perform certain H19 emulation functions such as character insert/delete and screen scroll. Without direct screen memory access, these func- tions would be painfully slow. Although Kermit-MS has been tested successfully on a variety of monochrome and color adap- ters and monitors, there may be combinations for which this method could cause video problems, such as snow. Should this occur, you can alleviate the problem by setting HEATH19 emula- tion OFF. In that case, however, you remove not only the problems, but also the desirable features of emulation. But Kermit-MS does permit you to load an external console device driver, such as IBM's ANSI.SYS, to provide any desired screen control. Here are the terminal emulation options for the systems presently sup- ported by Kermit-MS: System EscChar Cabilities Terminal Service IBM PC, XT ^] R M P K Heath19 emulation DEC Rainbow ^] R P K VT102 firmware HP-150 ^] R HP-2623 firmware Wang PC ^A Wang firmware Generic DOS ^] Depends on system Under Capabilities, R means rollback, M means mode line, P means printer control, and K means key redefinition. IBM PC/XT Kermit can disable Heath-19 emulation and use an external con- sole device driver like ANSI.SYS instead. When you first issue the CONNECT command, a message (on some systems, an 101 inverse video "mode line") will display the most important facts about the connection you've just established, so that you can quickly diagnose any problems. The items displayed in the mode line include the escape character, port number, the baud rate, the parity, the echo, and how to get help, for instance: +---------------------------------------------------------------------+ |EscChar:^],Port:1,Baud:9600,Parity:None,Echo:Remote,Type ^]? for Help| +---------------------------------------------------------------------+ The escape character is used to regain the attention of Kermit-MS. When you type the escape character, Kermit-MS waits for you to follow it with a single character command. For instance, the single-character-command "?" produces a list of available single character commands, such as this: ? Help -- prints the available single-character commands. C Close the connection and return to Kermit-MS prompt level. S Show the status of the connection. B Send a BREAK signal to the port. 0 (the digit zero) Send a NUL (ASCII 0) to the port. Q Temporarily quit logging the remote session. R Resume logging the remote session. M Toggle the mode line, i.e. turn it off if it is on & vice versa. ^] (or whatever you have set the escape character to be) Typing the escape character twice sends one copy of it to the con- nected host. Typing any other character (except the space bar, which is the "null command") after the escape character will cause Kermit-MS to beep, but will do no harm. The escape character can be changed to something other than Control-Rightbracket by using the SET ESCAPE command. Kermit-MS includes several advanced features for use during terminal emulation, including screen scroll, printer control, and key redefini- tions. Screen Scroll Kermit-MS provides several pages of screen memory, which may be scrolled up and down using keys as follows: Function IBM PC/XT Rainbow HP-150 Screen Down PgDn PrevScreen Prev Line Down Ctrl-PgDn Ctrl-PrevScreen Shift-UpArrow Screen Up PgUp NextScreen Next Line Up Ctrl-PgUp Ctrl-NextScreen Shift-DownArrow Top of Memory Home Bottom of Memory End There is presently no way to assign these functions to other keys. 102 Printer Control A locally attached printer may be controlled in the normal manner, on most systems. Pushing the "Print Screen" key (shifted on some systems) will cause the current contents of the screen to be printed or spooled; holding down CTRL while depressing Print Screen will start or stop the spooling of incoming characters to the printer. ^P or ^N are sent to the host during terminal emulation, and do not toggle printing, as they do when you're talking directly to DOS. CTRL-Print-Screen can be simulated with the Kermit-MS LOG PRN and CLOSE commands. Key Redefinitions Key redefinitions are useful for defining "keystroke macros" of login sequences, frequently issued commands, and so forth, and for setting up the terminal for use with host resident software designed to work with terminals that send predefined sequences from their function keys. For instance, here's a key redefinition file for arranging the DEC Rainbow keyboard into the normal ASCII keyboard layout: ; Make shift-comma send a left angle bracket set key scan 556 < ; Shift-period sends a right angle bracket set key scan 558 > ; Accent grave is where ESC is supposed to be set key scan 96 \33 ; Put accent grave on the ESC function key set key f11 ` The SET KEY facility may be used provide the PC with a "meta" key for use with editors like EMACS or TVEDIT that can use "meta characters" as commands. A meta key is a shift key whose effect is to turn on the 8th (parity) bit of the character. For instance, on the IBM PC the scan codes produced by holding down ALT together with other keys can be determined using SHOW KEY, and then 8-bit ASCII equivalents with the 8th bit turned on can be defined using SET KEY; if the scan code produced by typing ALT-a, i.e. the letter "a" (ASCII 141, octal) with the ALT key held down, is 2078 (decimal), you would set the META equivalent to 141+200=341 (octal), or "\341" in octal SET KEY notation: Kermit-MS>sho key Press a key: ALT-a Scan Code: 2078 Definition: Kermit-MS>set key scan 2078 Definition String: \341 Whenever you type ALT-a with this definition in effect, Kermit-MS will transmit octal 341, rather than 141. 103 9.5. Installation of Kermit-MS by Bill Catchings, Columbia University If you already have Kermit on your PC, you can use it to obtain new ver- sions of Kermit-MS when they appear on the central system at your site. If you do not have Kermit or any other reliable file capture facility on your PC, and there is no one from whom you can borrow a floppy disk to copy Kermit, then you should read the following instructions for in- itially "bootstrapping" Kermit-MS from a mainframe where it is stored onto your microcomputer. There are at least three methods of initially getting Kermit-MS onto your PC: 1. Try again to find a copy on diskette. 2. Use another file capture facility to get it. 3. Type in and run a bootstrapping program. 9.5.1. Try Again To Find A Kermit Disk Before explaining how to bootstrap Kermit onto your PC, a disclaimer must be made. Although a fair amount of thought and time has gone into these procedures, they are far from error free. If they were foolproof, there would be no need for a protocol such as Kermit. There are many places where things can go wrong, from something as simple as a typing mistake to something as unavoidable and probably inevitable as a com- munications line failure. By far the easiest and best way to install Kermit is from a floppy disk. Before you embark on any of the following procedures it is a good idea to check once again for a diskette to copy, even it it contains an old version of Kermit. The time you spend searching is likely to be far less frustrating than the time you spend trying to bootstrap Kermit by the methods described below. 9.5.2. Bootstrapping From the Communication Line If you can't find a diskette with Kermit on it, there are two other methods available for bootstrapping MS-DOS Kermit onto your PC. The first method is to use a file capture method or other file transfer protocol to transfer the file to your PC. Some systems come supplied with facilities like this, and various public domain or commercial packages are available. The second method requires you to type in your own downloading program. In either case, you must transmit the file from the system where it resides over a communication line and into your PC. Since version 2 of MS-DOS Kermit is much larger than version 1, it comes with a new bootstrapping procedure in which the executable program is encoded much more compactly than in the earlier "fix" files. The new encoding packs 3 .EXE file bytes into 4 printable characters in the MSxxx.BOO file, and also compresses adjacent zero bytes (of which there are many). The .BOO file contains only printable characters, to ensure that downloading can 104 take place regardless of parity or other peculariaries of the communica- tion channel. 9.5.2.1. Use An Existing File Capture Facility In the rest of this discussion of bootstrapping, the host-resident boot X.BOO file will be referred to as MSKERMIT.BOO. In fact, the actual name will depend on which MS-DOS system you are using -- MSIBMPC.BOO for the IBM PC or XT, MSRB100.BOO for the Rainbow 100, etc. Use your file capture facility, whatever it may be, to get the file MSKERMIT.BOO onto your PC's disk, but first make sure you have enough room for it. Once the file is on your disk, you must run the BASIC program MSPCTRAN.BAS to decode the file back into KERMIT.EXE. This program can be downloaded by the same method you used with MSKERMIT.BOO. The program looks on your current disk and directory for the file MSKERMIT.BOO and outputs KERMIT.EXE to the same place. KERMIT.EXE is about 80K bytes, so make sure there is space for it on your disk or else you will have to start the program over. Since the program will take about twenty minutes to completely translate the file you will want to avoid running it more than once. 9.5.2.2. Type In Your Own Bootstrap If you can't find some method for downloading the .BOO file and the BASIC program, the second way of bootstrapping Kermit is to use the programs MSPCBOOT.BAS and MSBOOT.FOR to download via your PC's asynchronous port from your host and translate it directly, "on the fly." You run the program MSBOOT.FOR on your host and then run the program MSPCBOOT.BAS in BASIC on your PC. The FORTRAN program sends the encoded .EXE file to the BASIC program, which decodes it and stores it in executable form on your current directory as KERMIT.EXE. A very rudimentary form of error checking is done to allow obviously corrupted records to be retransmitted. Follow this procedure: 1. First, you must establish a connection from your PC to the host system. A high speed connection is preferable; a "clean" line is preferable to a noisy one. In fact, a clean line is essential for this procedure. You must be able to log in to the host system over this connection. If your PC already has a terminal emulation facility, use that. If not, you might need to put your PC next to a real terminal and use that for logging in, then switch the connector to the PC at the crucial moment. If you are using a terminal, make sure the terminal and PC have their communication ports set to the same speed. 2. Ensure that the files MSBOOT.FOR and MSKERMIT.BOO are present on the host system. MSBOOT.FOR is listed below, in case you need to type it in. 3. Get back to your PC and type in MSPCBOOT.BAS on your PC; a listing appears below. There is no need to type in the com- ments (anything following an apostrophe); they are only there 105 to clarify what the program is doing. Check very carefully for errors. You should check line 80 in the program to see that it reflects the way your system is actually set up. If necessary, substitute the correct baud rate for the supplied rate of 9600, and if you are not using COM1: make that change as well. If you are downloading from an IBM or other half- duplex mainframe, leave line 1000 as it is; otherwise, replace it by a RETURN statement. If you type it in directly to BASIC make sure you save the program before you run it, so you won't have to type it in again in case of error. 4. Get back to your host system and compile MSBOOT.FOR, if it needs compiling. Define logical unit numbers 5 and 6 to be the controlling terminal, and logical unit 7 to be the file MSKERMIT.BOO. On VAX/VMS systems, for example, use these commands: $assign sys$input for005 $assign sys$output for006 $assign mskermit.boo for007 On a DECSYSTEM-20, do: @define 5: tty: @define 6: tty: @define 7: mskermit.boo On a DECsystem-10, do something like this: .assign tty: 5: .assign tty: 6: .assign dsk: 7: .rename for007.dat=mskerm.boo On an IBM system under VM/CMS, do this: .filedef 5 term ( lrecl 80 recfm v .filedef 6 term ( lrecl 80 recfm v .filedef 7 disk mskermit boo ( lrecl 77 recfm f perm 5. Set your host system up for downloading: - Ensure that your terminal does not automatically pause at the end of a screenful of output. For instance, on a DEC-20 you would issue the command "terminal no pause end-of-page". - Do whatever you can to disable messages from appearing at your terminal while these programs are running. This would include messages from other users, mail notifica- tion, alarms or alerts, system messages, and so forth. Such messages will interfere with the procedure, and probably render the result useless. - You should put your host terminal in "local echo" or "half duplex" mode, if possible. 106 6. Start the MSBOOT program on your host system. 7. Get back to the PC. If you have been using a terminal, switch the connector to the PC. 8. Now run the BASIC program, MSPCBOOT.BAS. This procedure will take at least twenty minutes and possibly longer depending on line speed. Watch your modem and/or disk lights for reas- surance that something is happening. By using one of these installation methods, you should now have a work- ing version of Kermit. If you experience any problems or quirky be- havior with the program, it's possible that some part of it was cor- rupted during the downloading procedure. Perhaps enough usable code remains to allow you to transfer MSKERMIT.EXE from the host. If not, you will have to repeat the downloading procedure. Once you have Kermit-MS on your disk, you should make the disk available to other users for copying, so that they can be spared the tedium of this bootstrap procedure. Here is a listing of MSPCBOOT.BAS. The "outdented" PRINT statements with line numbers ending in 5 may be included if you want incoming records to be displayed on the screen. You don't need to include the comments. 1 'Run this program on the PC in conjunction with a Fortran program 2 '(MSBOOT.FOR) on the mainframe to download Kermit to the PC. This 3 'program will run for about thirty minutes, depending on line speed. 4 ' Bill Catchings, June 1984 5 ' Columbia University Center for Computing Activities 10 t$ = time$ ' Save the time. 20 defint a-z ' All integer to gain some speed. 30 n$ = chr$(0) 40 z = asc("0") 50 t = asc("~")-z 60 def fnuchr%(a$)=asc(a$)-z 70 open "com1:9600,s,7,1,cs,ds,cd" as #1 100 print#1,"O ," ' Char constants "O", " " and "," 110 input#1,f$ 120 if len(f$) < 5 then goto 110 ' In case the host echos the ACK. 130 input#1,n 135 print f$+" "+str$(n) 140 if n > 20 then goto 900 150 open f$ for output as #2 160 print "Outputting to "+f$ 170 goto 300 ' Correct version of the file. 200 gosub 1000 ' Do turnaround char processing 210 print#1,"NO" ' Tell host data was incorrect. 220 goto 320 107 300 gosub 1000 ' Do turnaround char processing 310 print#1,"OK" ' Say the line was all right. 320 input#1,x$ 330 if len(x$) < 5 then goto 320 ' In case the host echos ACK/NAK 340 input#1,n 345 print x$+" "+str$(n) 350 if len(x$) <> n then goto 200 ' Length doesn't match, NAK it. 360 if x$ = "&&&&&&&&&&" then goto 800 ' End of file? 370 y$ = "" ' Set output string to null. 380 goto 500 400 print#2,y$; ' Print the output string. 410 goto 300 ' Go get another line. 500 if len(x$) = 0 goto 400 ' Done with input string? 510 a = fnuchr%(x$) 520 if a = t then goto 700 ' Null repeat character? 530 q$=mid$(x$,2,3) ' Get the quadruplet to decode. 540 x$=mid$(x$,5) 550 b = fnuchr%(q$) 560 q$ = mid$(q$,2) 570 c = fnuchr%(q$) 580 q$ = mid$(q$,2) 590 d = fnuchr%(q$) 600 y$ = y$ + chr$(((a * 4) + (b \ 16)) and 255) ' Decode the quad. 610 y$ = y$ + chr$(((b * 16) + (c \ 4)) and 255) 620 y$ = y$ + chr$(((c * 64) + d) and 255) 630 goto 500 ' Get another quad. 700 x$ = mid$(x$,2) ' Expand nulls. 710 r = fnuchr%(x$) ' Get the number of nulls. 715 print " Null: ",r 720 x$ = mid$(x$,2) 730 for i=1 to r ' Loop, adding nulls to string. 740 y$ = y$ + n$ 750 next 760 print#2,y$; ' Print the nulls. 770 y$ = "" ' Clear the output buffer. 780 goto 500 800 print "Processing complete, elapsed time: "+t$+" to "+time$ 810 print "Output in "+f$ 820 close #1,#2 830 goto 9999 900 print "?The format of the BOO file is incorrect" 910 goto 820 1000 x$ = input$(1,#1) ' Make this line RETURN for full-duplex 1010 if x$ <> chr$(17) then goto 1000 ' Loop for a turn around char. 1020 return 9999 end 108 Here is a listing of MSBOOT.FOR, in case you can't find it on your host system: C This Fortran program should be run on the mainframe in conjunction C with a Basic program (MSPCBOOT.BAS) on the PC to transfer C MSKERMIT.BOO to the PC and translate it into KERMIT.EXE. This C program uses a very rudimentary technique to try to insure that C the characters it sends arrive correctly. It just sends a count C of the number of characters sent after each line. In this way any C errors of character loss or insertion will be caught. If a C character is just corrupted it will not be caught. Hopefully if C this happens it will be in a non-critical part of the KERMIT.EXE C file. The reason a simple checksum was not used was so that this C program could run on machines using either EBCIDIC or ASCII C characters. This program should take about thirty minutes to run. C This program assumes that 5 and 6 are directed to the terminal and C 7 is directed to the file MSKERMIT.BOO. INTEGER LINE(77), ACK(4), CHECK, OK, SPACE, COMMA WRITE(6,100) 100 FORMAT(' Ready to transfer data, now run MSPCBOOT.BAS on the PC.') C Get characters for constants (character constants are rough in C some FORTRANs!) READ (5,200) OK, SPACE, COMMA, ACK 200 FORMAT(4A1) GO TO 30 C Get terminal handshake. 10 READ (5,200)ACK C Did the other side like it? (Did they send OK?) IF (ACK(1) .NE. OK) GO TO 50 C Yes, get new line from file. 20 READ (7,300,END=99)LINE 300 FORMAT(77A1) C Count the characters as some rudimentary check for noise. I = 1 30 IF (LINE(I) .EQ. SPACE) GO TO 40 I = I + 1 GO TO 30 C Put in a comma followed by the count. 40 LINE(I) = COMMA C Write to TTY. 50 WRITE (6,400)LINE,I-1 400 FORMAT(' ',77A1,I2) GOTO 10 109 C Send good-bye message. 99 WRITE (6,500) 500 FORMAT(' ',10('&'),',10') STOP END 9.6. Compatibility with Older Versions of MS-DOS Kermit MS-DOS Kermit supports many different systems. Like CP/M-80 KERMIT, this support was added to the program piecemeal, at many sites, using conditional assembly. However, before allowing the program to grow into a complicated monolith like CP/M-80 KERMIT, we have broken the program up into separate modules, with system dependencies isolated into a single module consisting of compact collections of low-level primitives for console and port i/o. The last monolithic (single source file) release of MS-DOS Kermit was 1.20. To this and earlier versions was added support for systems like the Seequa Chameleon, the Tandy 2000, the Victor 9000, the Heath/Zenith 100, and others. Eventually, support for these systems may be in- tegrated with the new modular version. Meanwhile, implementations based on these old versions will have at least the following incompatibilies from the version described here: - RECEIVE filespec is used instead of GET filespec. There is no GET command in older versions, and no way to specify a new name for an incoming file. - No LOCAL or REMOTE commands. - No 8th-bit prefixing, repeat counts, CRCs or 2-character checksums. - No TAKE or initialization files. - No command macros or command line arguments. - No terminal session logging. and others, depending on the specific version. 9.7. What's Missing Kermit-MS has plenty of room for improvement. Features that need to be improved or added include: - A built-in facility for sending files "raw" to the remote sys- tem, obeying current settings for parity, flow control, hand- shake, and so forth. This might include a script interpreta- tion facility to allow remote sessions to be conducted automatically. For the present, this can be accomplished with a user-supplied program invoked with the Kermit-MS RUN com- mand. - Additional functionality when running in server mode -- directory listings, file deletion, execution of DOS com- mands, etc. 110 - More commands when talking to remote servers -- REMOTE RENAME, COPY, STATUS, WHO, etc. - Filename conversion options (normal form, handling of fully qualified filespecs, etc.). - Transaction file logging. - Improved command parsing; for instance, accept default values for omitted trailing fields. - A better built-in help facility. - Support for Kermit file attribute packets. - The Kermit-MS program is quite large. Much of the size is due to the deliberate decision to provide support for versions of MS-DOS prior to 2.0. At some point, this support should be removed. This will not only reduce the size of the program considerably, but also provide much more flexibility. 9.8. Program Organization Kermit-MS version 2 is composed of separate assembler source files, as- sembled separately, and linked together. The modules are: System/Device Independent: MSKERM.ASM Main program MSSEND.ASM File sender MSRECV.ASM File receiver MSSERV.ASM Server operation MSFILE.ASM File i/o MSCMD.ASM Command parser MSTERM.ASM CONNECT command MSCOMM.ASM Communications port buffering & flow control MSSET.ASM SET, SHOW, and STATUS commands MSDEFS.H Data structure definitions and equates System/Device Dependent: MSXxxx.ASM System-dependent code for system xxx MSYxxx.ASM System-dependent screen and keyboard code MSZxxx.ASM Modem control (modem-dependent) The modular organization allows easier modification of the program, quicker transfer of modified portions from system-to-system. The modules are designed to be well-defined and self-contained, such that they can be easily replaced. For instance, someone who prefers windows and mice to typing commands could replace the command parsing module without having to worry about the effect on the other modules. To assemble any of the kermit modules, file MSDEFS.H must be on the default disk. 111 All the Kermit implementations require the modules MSCMD, MSCOMM, MSFILE, MSKERM, MSRECV, MSSEND, MSSERV, MSSET, MSTERM. The IBM PC version requires MSXIBM and MSYIBM as well. The Rainbow version requires MSXRB and MSXDMB; MSXDMB must be the first object file given to the linker for Kermit to link properly for the Rainbow. The HP150 version requires MSXHP150, the Wang version requires MSXWNG, and the generic version requires MSXGEN. Once all the required object modules exist, they may be linked together to produce Kermit. For example, on the Rainbow: A>link Microsoft Object Linker V2.00 (C) Copyright 1982 by Microsoft Inc. Object Modules [.OBJ]: msxdmb mskerm msxrb mscomm msset mssend + msrecv msserv msfile msterm mscmd Run File [MSXDMB.EXE]: kermit List File [NUL.MAP]: kermit A> 9.9. Adding Support For New Systems You can bring Kermit-MS to systems that are not explicitly supported in one of two ways -- attempt to run the "generic" MS-DOS Kermit on it, or add explicit code to support your system. 9.9.1. Generic MS-DOS Kermit To get started with Kermit on a new system, try running "generic" MS-DOS Kermit; in many cases, it will run as is. The generic version ac- complishes all its port and console i/o through DOS calls, and during terminal connection does not attempt to emulate any particular kind of terminal. In some cases, the generic version may still require some fiddling to run on a new system; for instance, different systems refer to their communication ports in different ways -- COM1, AUX, etc. It attempts to do this automatically by trying various DOS file handles for the communication port, and asking you to supply one if it does not succeed. Generic MS-DOS Kermit will probably run no faster than 1200 baud, and it only works with DOS 2.0 or later. 112 9.9.2. Adding System-Dependent Code The following is a guide to the system dependent module of Kermit-MS. SPECIFICATION FOR KERMIT SYSTEM-DEPENDENT MODULES by Jeff Damens, Columbia University All the system-independent global data structures used in Kermit-MS are defined in the file MSDEFS.H. The routine MSXxxx.ASM contains system-dependent support for system xxx, except for terminal emulation, which is in MSXxxx.ASM, described below. The routines in the MSX module may change any registers but the stack pointer and segment registers, unless otherwise noted. A routine that returns via a RET instruction is said to return normally; a routine that skip returns is one that returns to three bytes past the normal return address. Global variables that must be defined in the system-dependent module: XXOFSNT byte. This should be set to a non-zero value if we are doing flow control and have sent an XOFF character to the remote host, zero otherwise. MACHNAM byte. A $-terminated string identifying the machine this version of Kermit is for; it is printed when Kermit starts up. SETKTAB byte. A keyword table associating terminal key names to 16-bit scan code values, used in the set key command. If the kermit version can accept arbitrary decimal values as scan codes, the word "SCAN" should appear in the table with a scan value of -1. If key redefinition is not implemented, the first byte of the table should be a zero. SETKHLP byte. A $-terminated string to be printed when ? is typed in the SET KEY command. This is usually simply a list of the key names in SETKTAB. SETKHLP must be defined even if key redefinition is not implemented, to satisfy the linker; if key redefinition is not imple- mented, SETKHLP will never be displayed. COUNT word. The number of characters in the serial input buffer, if known. This is how Kermit knows to send an XON if the serial handler has sent an XOFF. If the num- ber of characters in the buffer isn't known, COUNT should be 0. These are the required entry points for the system dependent dependent module MSXxxx.ASM. 113 SERINI Parameters None. Returns Normally, no return value. Description Perform any initialization that must be done before the serial port can be used, including setting baud rate, interrupt vectors, etc. Parity and baud rate should be set according to the values in the PORTINFO structure. The external variable PORTVAL points to the PORTINFO structure for the current port. Calling SERINI more than once without an intervening call to SERRST should have no effect. SERRST Parameters None. Returns Normally, no return value. Description Undoes any initialization done by SERINI, including resetting the serial port, restoring any interrupt vec- tors changed by SERINI, etc. Calling this more than once without an intervening call to SERINI should be harmless. CLRBUF Parameters None. Returns Normally, no return value. Description Remove and discard from the serial port's input buffer any characters sent by the remote host that have not yet been read by Kermit, and set COUNT to 0. This is used before a file transfer to flush NAK's that accumulate in the buffer when the remote host is in server mode. 114 OUTCHR Parameters A character in AH. Returns Skip returns if the character has been transmitted; returns normally if the character can not be transmitted because of a hardware error. Description Sends the character in AH out the currently selected serial port. OUTCHR can assume that SERINI will have been called previously. OUTCHR should call the external routine DOPAR to set the parity of the character if the communications hardware doesn't automatically set parity. Flow control should be honored; the external variable PORTVAL contains a pointer to a PORTINFO struc- ture (as defined in MSDEFS.H) containing the current flow control definitions. COMS Parameters None. Returns Normally if a parse error is encountered, skip returns otherwise. Description Called by the SET PORT command. On a machine with mul- tiple serial ports, COMS should parse for the name or number of a serial port and make that the port used by succeeding calls to SERINI, PRTCHR, OUTCHR, and SERRST. It should set the external variable PORTVAL to point to one of the external port structures PORT1 or PORT2, and set COMFLG in the FLAGS structure to 1 for port one, 0 for port 2. For implementations that use only one serial port, COMS should print a message to that effect and skip return. VTS Parameters None. Returns Normally if a parse error is encountered, skip returns otherwise. Description Parses for an ON or OFF, sets HEATH-19 emulation while in terminal emulation appropriately. The VTFLG field of the FLAGS structure should be set non-zero if HEATH-29 emulation is on, zero otherwise. If HEATH-19 emulation is not done, VTS should print a message and skip return. 115 DODEL Parameters None. Returns Normally, no return value. Description Erases the character immediately to the left of the cur- sor from the screen, then backs up the cursor. CTLU Parameters None. Returns Normally, no return value. Description Move the cursor to the left margin, then clear the line. CMBLNK Parameters None. Returns Normally, no return value. Description Clears the screen and homes the cursor. LOCATE Parameters None. Returns Normally, no return value. Description Homes the cursor. LCLINI Parameters None. Returns Normally, no return value. Description Performs any system-dependent initialization required by this implementation. 116 PRTCHR Parameters None. Returns Normally, with the next character from the currently selected serial port in AL. Skip returns if no charac- ter is available. Description Reads the next character from the current serial port. PRTCHR can assume SERINI has been called previously, and should handle flow control correctly. DOBAUD Parameters None. Returns Normally, no return value. Description Sets the baud rate for the current port. The baud rate should be obtained from the BAUD field of the PORTINFO structure, pointed to by the external variable PORTVAL. CLEARL Parameters None. Returns Normally, no return value. Description Clears from the cursor to the end of the current line. DODISK Parameters None. Returns Normally, no return value. Description Sets the external variable DRIVES to the number of disk drives attached to the machine. GETBAUD Parameters None. Returns Normally, no return value. Description Store current baud rate of the currently selected port in the BAUD field of the current PORTINFO structure, which is pointed to by PORTVAL. If the baud rate is to default to a particular value, this routine can store that value into the BAUD field instead. 117 BEEP Parameters None. Returns Normally, no return value. Description Rings the terminal bell. PUTHLP Parameters A pointer to a string in AX. Returns Normally, no return value. Description Writes the null-terminated string given in AX to the terminal. This is used to display help and status mes- sages. The IBM and Rainbow versions write the string in a reverse video box. PUTMOD Parameters A pointer to a string in AX. Returns Normally, no return value. Description Writes the null-terminated string given in AX to the last line of the screen, in inverse video if possible. CLRMOD Parameters None. Returns Normally, no return value. Description Clears the line written by PUTMOD. POSCUR Parameters Row in DH, column in DL. Returns Normally, no return value. Description Positions the cursor to the row and column given in DX. Rows and columns both originate at 0 (not 1!). 118 SENDBR Parameters None. Returns Normally, no return value. Description Send a break to the current serial port. SHOWKEY Parameters Pointer to a terminal argument block in AX (see TERM below). Returns Normally, with a string pointer in AX and the length of the string in CX. Description Called by the SHOW KEY command. Reads a key from the terminal and returns a string containing implementation-dependent information about the key. In the usual case, the string contains the key's (machine-dependent) scan code, and the key's definition (if any) from the terminal argument block. The length of the returned string should be returned in CX. The string may contain any characters; unprintable charac- ters will be quoted when the string is printed. If the implementation does not support key redefinition, SHOW- KEY may return a static string saying so. TERM Parameters Pointer to terminal argument block in AX. Returns Normally, no return value. Description Do terminal emulation, based on argument block described below... The terminal emulator is supplied in the file MSYxxx.ASM. The terminal argument block passed to the terminal emulator has the following fields: FLGS Byte containing flags. Flags are: SCRSAM (80H) If on, the terminal emulator shouldn't re-display the screen when entered. CAPT (40H) Capture output. If on, the routine passed in field CAPTR is called with each character sent to the screen. EMHEATH (20H) Emulate a Heath-19 terminal if on. 119 HAVTT (10H) A key redefinition table is present. TRNCTL (08H) Print control character X as ^X (useful for debugging). MODOFF (04H) Do not display emulator mode line if on. LCLECHO (01H) Echo keyboard characters on the screen in addition to sending them to the port. PRT Port to use for terminal emulation, used only in mode line. This is just a copy of COMFLG in FLAGS. COLS Number of columns on screen. ROWS Number of rows on screen. CAPTR Routine to call to with each character sent to the screen if CAPT flag is on. Characters are passed in AL. BELLD Bell divisor (used only on IBM). KLEN Number of keys in key redefinition table, if HAVTT flag is on. KTAB Address of key redefinition table. The key redefinition table is a table of KLEN 16-bit scan codes. Each (machine dependent) scan code represents a key that is redefined. KRPL Address of key replacement table. The key replacement table parallels the key redefinition table given in KTAB. Entries in the replacement table are 16-bit pointers to redefinitions. Each redefinition has a one-byte length, followed by the definition. ESCC Escape character (single byte). When this character is typed to the emulator, it should return. BAUDB byte. Bits describing the baud rate so it can be printed on the mode line. This is a copy of the BAUD field in the PORTINFO structure. Currently used only on the IBM. See MSDEFS.H for possible values. PARITY byte. Current parity to print on the mode line. This is a copy of PARFLG in the PORTINFO structure. Cur- rently used only on the IBM. See MSDEFS.H for possible values. 120 9.10. Heath/Zenith-19 Control Codes The Heath/Zenith-19 terminal is equivalent to the DEC VT52 with exten- sions for line and character insertion and deletion. Items marked with an asterisk are not currently supported by Kermit-MS H19 emulation. Cursor Functions Sequence Mnemonic Definition ESC H HCUH Cursor Home ESC C HCUF Cursor Forward ESC D HCUB Cursor Backward ESC B HCUD Cursor Down ESC A HCUU Cursor Up ESC I HRI Reverse Index *ESC n HCPR Cursor Position Report *ESC j HSCP Save Cursor Position *ESC k HRCP Set Cursor to Previously Saved Position ESC Y HDCA Direct Cursor Addressing, 1-based: 31+line# 31+col# (same as VT52) Erasing and Editing Sequence Mnemonic Definition ESC E HCD Clear Display (Shift Erase) ESC b HBD Erase Beginning of Display ESC J HEOP Erase to End of Page (Erase Key) ESC l HEL Erase Entire Line ESC o HEBL Erase Beginning of Line ESC K HEOL Erase to End of Line ESC L HIL Insert Line ESC M HDL Delete Line ESC N HDCH Delete Character ESC @ HEIM Enter Insert Character Mode ESC O HERM Exit Insert Character Mode Configuration Sequence Mnemonic Definition *ESC z HRAM Reset to Power-Up Configuration *ESC r Bn HMBR Modify Baud Rate: Bn= A=110, B=150, C=300, D=600, E=1200, F=1800, G=2000, H=2400, I=3600, J=4800, K=7200, L=9600, M=19200 *ESC x Ps HSM Set Mode(s): Ps= 1 = Enable 25th line 2 = No key click 3 = Hold screen mode 4 = Block cursor 5 = Cursor off 121 6 = Keypad shifted 7 = Alternate keypad mode 8 = Auto line feed on CR 9 = Auto CR on line feed *ESC y Ps HRM Reset mode(s): Ps= 1 = Disable 25th line 2 = Enable key click 3 = Exit hold screen mode 4 = Underscore cursor 5 = Cursor on 6 = Keypad unshifted 7 = Exit alternate keypad mode 8 = No auto line feed 9 = No auto CR *ESC < HEAM Enter ANSI Mode Modes of Operation Sequence Mnemonic Definition *ESC [ HEHS Enter Hold Screen Mode *ESC \ HXHS Exit Hold Screen Mode ESC p HERV Enter Reverse Video Mode ESC q HXRV Exit Reverse Video Mode *ESC F HEGM Enter Graphics Mode *ESC G HXGM Exit Graphics Mode *ESC t HEKS Enter Keypad Shifted Mode *ESC u HXKS Exit Keypad Shifted Mode *ESC = HAKM Enter Alternare Keypad Mode *ESC > HXAM Exit Alternate Keypad Mode Additional Operations Sequence Mnemonic Definition *ESC } HDK Keyboard Disable *ESC { HEK Keyboard Enable ESC v HEWA Wrap Around at End of Line ESC w HXWA Discard at End of Line ESC Z HID Identify as VT52 (ESC / K) *ESC ] HX25 Transmit 25th Line *ESC # HXMP Transmit Page The Heath-19 transmits the following sequences, but it will not respond to them if they are received. Kermit-MS will transmit them only if they are programmed with SET KEY. ESC S HF1 Function Key #1 ESC T HF2 Function Key #2 ESC U HF3 Function Key #3 ESC V HF4 Function Key #4 ESC W HF5 Function Key #5 122 ESC P HF7 Function Key #7 ESC Q HF8 Function Key #8 ESC R HF9 Function Key #9 123 Index RECEIVE 86 ANSI.SYS 94 Recognition 83 Autodialer 100 Smart Modem 100 Batch Operation of Kermit-MS 84 Baud Rate 92 Telenet 95 Beeper 92 Timeout 98 Bell 92 Binary Files 96 Warning 98 Wildcard 81 Cancelling a File Transfer 86, 87 Command Files 98 Command Macro 99 CONNECT 100 Control-X 86, 87 Control-Z 86, 87 Debugging 93 DEFINE 99 DO Command 99 Eighth-Bit Prefix 96 EMACS 102 End Of File 82, 93 EOF 93 Escape Character for CONNECT 94 File Renaming 98 File Warning 98 Flow Control 94 Generic MS-DOS Kermit 111 Handshake 94 Heath-19 Terminal Emulation 94, 100, 120 Help 83 IBM PC 79 Incomplete File Disposition 87 Key Redefinition 95 Local Echoing 95 Macro 99 META Key 102 Mode Line 100 Modem 100 MS-DOS 79 Parity 95 PC-DOS 79 i Table of Contents 9. MS-DOS KERMIT 79 9.1. The MS-DOS File System 80 9.1.1. File Specifications 80 9.1.2. File Formats 81 9.2. Program Operation 82 9.3. Kermit-MS Commands 84 9.3.1. Commands for File Transfer 85 9.3.2. Commands for Connecting and Disconnecting 88 9.3.3. Commands for File Management 89 9.3.4. The SERVER Command 91 9.3.5. The SET Command 92 9.3.6. The SHOW Command 98 9.3.7. Command Macros 99 9.4. Terminal Emulation 100 9.5. Installation of Kermit-MS 103 9.5.1. Try Again To Find A Kermit Disk 103 9.5.2. Bootstrapping From the Communication Line 103 9.5.2.1. Use An Existing File Capture Facility 104 9.5.2.2. Type In Your Own Bootstrap 104 9.6. Compatibility with Older Versions of MS-DOS Kermit 109 9.7. What's Missing 109 9.8. Program Organization 110 9.9. Adding Support For New Systems 111 9.9.1. Generic MS-DOS Kermit 111 9.9.2. Adding System-Dependent Code 112 9.10. Heath/Zenith-19 Control Codes 120 Index 123 //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 640 mskermit.doc /bin/echo -n ' '; /bin/ls -ld mskermit.doc fi /bin/echo 'Extracting msbuild.hlp' sed 's/^X//' <<'//go.sysin dd *' >msbuild.hlp To assemble any of the kermit modules, file MSDEFS.H must be on the default disk. All the Kermit implementations require the modules MSCMD, MSCOMM, MSFILE, MSKERM, MSRECV, MSSEND, MSSERV, MSSET, MSTERM. The IBM PC version requires MSXIBM and MSYIBM as well. The Rainbow version requires MSXRB and MSXDMB; MSXDMB must be the first object file given to the linker for Kermit to link properly. The HP150 version requires MSXHP150, the Wang version requires MSXWNG, and the generic version requires MSXGEN. Once all the required object modules exist, they may be linked together to produce Kermit. For example, on the Rainbow: A>link Microsoft Object Linker V2.00 (C) Copyright 1982 by Microsoft Inc. Object Modules [.OBJ]: msxdmb mskerm msxrb mscomm msset mssend msrecv + msserv msfile msterm mscmd Run File [MSXDMB.EXE]: kermit List File [NUL.MAP]: kermit A> //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 640 msbuild.hlp /bin/echo -n ' '; /bin/ls -ld msbuild.hlp fi /bin/echo 'Extracting msboot.for' sed 's/^X//' <<'//go.sysin dd *' >msboot.for C This Fortran program should be run on the mainframe in conjunction C with a Basic program (MSPCBOOT.BAS) on the PC to transfer C MSKERMIT.BOO to the PC and translate it into KERMIT.EXE. This C program uses a very rudimentary technique to try to insure that C the characters it sends arrive correctly. It just sends a count C of the number of characters sent after each line. In this way any C errors of character loss or insertion will be caught. If a C character is just corrupted it will not be caught. Hopefully if C this happens it will be in a non-critical part of the KERMIT.EXE C file. The reason a simple checksum was not used was so that this C program could run on machines using either EBCDIC or ASCII C characters. This program should take about thirty minutes to run. C C This program assumes that 5 and 6 are directed to the terminal and C 7 is directed to the file MSKERMIT.BOO. C C Bill Catchings, Columbia University Center for Computing Activities C June 1984 (Revised September 1984) C INTEGER LINE(77), ACK(4), CHECK, OK, SPACE, COMMA WRITE(6,100) 100 FORMAT(' Ready to transfer data, now run MSPCBOOT.BAS on the PC.') C Get characters for constants (character constants are rough in C some FORTRANs). READ (5,200) OK, SPACE, COMMA, ACK 200 FORMAT(4A1) C The following statement has been changed from GO TO 30. GO TO 20 C Get terminal handshake. 10 READ (5,200)ACK C Did the other side like it? (Did they send OK?) IF (ACK(1) .NE. OK) GO TO 50 C Yes, get new line from file. 20 READ (7,300,END=99)LINE 300 FORMAT(77A1) C Count the characters as some rudimentary check for noise. I = 1 30 IF (LINE(I) .EQ. SPACE) GO TO 40 I = I + 1 GO TO 30 C Put in a comma followed by the count. 40 LINE(I) = COMMA C Write to TTY. 50 WRITE (6,400)LINE,I-1 400 FORMAT(' ',77A1,I2) GOTO 10 C Send good-bye message. 99 WRITE (6,500) 500 FORMAT(' ',10('&'),',10') STOP END //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 640 msboot.for /bin/echo -n ' '; /bin/ls -ld msboot.for fi /bin/echo 'Extracting mspcboot.bas' sed 's/^X//' <<'//go.sysin dd *' >mspcboot.bas 1 'Run this program on the PC in conjunction with a Fortran program 2 '(MSBOOT.FOR) on the mainframe to download Kermit to the PC. This 3 'program will run for about thirty minutes, depending on line speed. 4 ' Bill Catchings, June 1984 (Revised Sept 1984) 5 ' Columbia University Center for Computing Activities 10 t$ = time$ ' Save the time. 20 defint a-z ' All integer to gain some speed. 30 n$ = chr$(0) 40 z = asc("0") 50 t = asc("~")-z 60 def fnuchr%(a$)=asc(a$)-z 70 open "com1:9600,s,7,1,cs,ds,cd" as #1 100 print#1,"O ,2" ' Char constants "O", " " and "," 110 input#1,f$ 120 if len(f$) < 5 then goto 110 ' In case the host echos the ACK. 130 input#1,n 135 print f$+" "+str$(n) 140 if n > 20 then goto 900 150 open f$ for output as #2 160 print "Outputting to "+f$ 170 goto 300 ' Correct version of the file. 200 gosub 1000 ' Do turnaround char processing 210 print#1,"NO" ' Tell host data was incorrect. 220 goto 320 300 gosub 1000 ' Do turnaround char processing 310 print#1,"OK" ' Say the line was all right. 320 input#1,x$ 330 if len(x$) < 5 then goto 320 ' In case the host echos ACK/NAK 340 input#1,n 345 print x$+" "+str$(n) 350 if len(x$) <> n then goto 200 ' Length doesn't match, NAK it. 360 if x$ = "&&&&&&&&&&" then goto 800 ' End of file? 370 y$ = "" ' Set output string to null. 380 goto 500 400 print#2,y$; ' Print the output string. 410 goto 300 ' Go get another line. 500 if len(x$) = 0 goto 400 ' Done with input string? 510 a = fnuchr%(x$) 520 if a = t then goto 700 ' Null repeat character? 530 q$=mid$(x$,2,3) ' Get the quadruplet to decode. 540 x$=mid$(x$,5) 550 b = fnuchr%(q$) 560 q$ = mid$(q$,2) 570 c = fnuchr%(q$) 580 q$ = mid$(q$,2) 590 d = fnuchr%(q$) 600 y$ = y$ + chr$(((a * 4) + (b \ 16)) and 255) ' Decode the quad. 610 y$ = y$ + chr$(((b * 16) + (c \ 4)) and 255) 620 y$ = y$ + chr$(((c * 64) + d) and 255) 630 goto 500 ' Get another quad. 700 x$ = mid$(x$,2) ' Expand nulls. 710 r = fnuchr%(x$) ' Get the number of nulls. 715 print " Null: ",r 720 x$ = mid$(x$,2) 730 for i=1 to r ' Loop, adding nulls to string. 740 y$ = y$ + n$ 750 next 760 print#2,y$; ' Print the nulls. 770 y$ = "" ' Clear the output buffer. 780 goto 500 800 print "Processing complete, elapsed time: "+t$+" to "+time$ 810 print "Output in "+f$ 820 close #1,#2 830 goto 9999 900 print "?The format of the BOO file is incorrect" 910 goto 820 1000 x$ = input$(1,#1) ' Make this line RETURN for full-duplex 1010 if x$ <> chr$(17) then goto 1000 ' Loop for a turn around char. 1020 return 9999 end//go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 640 mspcboot.bas /bin/echo -n ' '; /bin/ls -ld mspcboot.bas fi
csitc@ucbopal.CC.Berkeley.ARPA (10/08/84)
It is also possible to get source to ALL the Kermits (not just MSDOS) by anonymous FTP to columbia-20.ARPA, with the usual "guest" password. The files themselves are in ps:<KERMIT>file. They also have have full documentation, including a manual to the protocol used in Kermit, for those interested in writing their own versions.... (For those who may not know what Kermit is, it is essentially a file- transfer protocol used for communications between micros and large machines. Most implementations also include some kind of terminal emulation, such as for a vt52 or h/z19.)
wdn@hp2610.UUCP (10/29/84)
I'm having a problem with the ms-kermit sources that were posted to the net recently. The file msfile.asm will not assemble on the hp150. Two of the errors seem to be related to addressing modes (LEA instructions), but the rest were undefined symbols. The file seems to be corrupted in the data declaration section. PLEASE! If anyone has a good copy of this file, will you send me a copy? Thanks in advance... Bill Nestor hp St. Louis ihnp4!wuphys!hp2610!wdn