; CP4TT.ASM ; KERMIT - (Celtic for "FREE") ; ; This is the CP/M-80 implementation of the Columbia University ; KERMIT file transfer protocol. ; ; Version 4.0 ; ; Copyright June 1981,1982,1983,1984,1985 ; Columbia University ; ; Originally written by Bill Catchings of the Columbia University Center for ; Computing Activities, 612 W. 115th St., New York, NY 10025. ; ; Contributions by Frank da Cruz, Daphne Tzoar, Bernie Eiben, ; Bruce Tanner, Nick Bush, Greg Small, Kimmo Laaksonen, Jeff Damens, and many ; others. ; ; This file contains the code for the TRANSMIT and CONNECT commands, ; which communicate with a host which is not running KERMIT. ; ; revision history: ; edit 4: 13-Jan-85 by Vanya J.Cooper Pima Commun. College Tel: 602-884-6809 ; ;pcc002 28-Dec-84 modules:cp4tt,cp4utl ; Add connect mode P command to toggle printer on ; and off. Conflicts with "official" recommended commands ; in protocol manual, but I don't think CP/M will ever get ; a PUSH command. ; ;pcc003-pcc005 2-Jan-85 vjc modules:cp4mit,cp4tt,cp4utl ; These edits must all be installed together and change the way ; logging is handled. The log file spec is moved to a separate ; fcb, and not opened until an actual CONNECT command is given. ; This takes care of a NASTY bug that if you used any other file ; command between the LOG and CONNECT, the log file would get ; written over the last file used. This also allows logging to ; be "permanently" enabled until an CLOSE (new command) for all ; CONNECT sessions, like most other kermits do. If a log file ; already exists, it will be appended to. Also add two new ; CONNECT mode commands Q to suspend logging and R to ; resume. R means something else during TRANSMIT, but ; logging is never on then, so there shouldn't be any conflict. ; I also changed the write code, so that it can handle one more ; character after the XOFF is send to stop the host. This allows ; a little "slop" for systems that don't stop immediately (such ; as TOPS10), but it didn't help much. ; ;pcc008 2-Jan-85 vjc modules:cp4def,cp4tt,cp4utl ; Keyboard input during CONNECT mode can get locked out if ; there is enough input from the modem port to keep prtchr ; busy. This can happen for example, if the printer is running ; at the same speed as the modem line, leaving you helpless to ; turn it off or abort the host. Add a fairness count, so that ; at least every prfair characters we look at console input. ; ;pcc012 4-Jan-85 vjc modules:cp4mit,cp4tt,cp4utl ; Use the big buffer for the log file. Move the log file back ; into the common fcb and only save the drive, name, and ; extension between connects. Add new routines to cp4utl to ; create or append to an existing file, and to conditionally ; advance buffers only if in memory. Remove edit pcc003 that ; allows one more character after the xoff, since it didn't ; really work very well and does not fit in well with the way ; the buffer advancing routines are set up. If someone still ; thinks this would be useful, it could be put back in with a ; little more work. ; ; While testing this edit, I also noticed another bug that ; the command parsing routines do not limit or check the ; length of command lines or file specs, trashing what ever ; comes after them. Currently because of where the fcb and ; command buffer are located, this does not usually cause a ; problem, but could if an extremely long line was typed in, ; or in the future multiple fcbs defined elsewhere in memory ; were used. Maybe this should be put on the bug list ; somewhere. ; ; edit 3: July 27, 1984 ; Allow assembly with LASM: to CP4TT is linked by CP4PKT, and links ; to CP4CPM; remove exclamation points so as not to confuse LASM. ; Add Toad Hall TACtrap to TRANSMIT command (TAC intercept character ; is only doubled if it's data; when typed by the user, they're not ; automatically doubled) ; ; edit 2: June 7, 1984 ; formatting and documentation; add module version number; make sure ; console is selected when leaving intchr. ; ; edit 1: May, 1984 ; extracted from CPMBASE.M80 version 3.9; modifications are described ; in the accompanying .UPD file. ttver: db 'CP4TT.ASM (4) 13-Jan-85$' ; This is the TRANSMIT command. It attempts to send a file, even ; though there is no KERMIT on the other side. ; here from: kermit xmit: mvi a,cmofi ;Parse an input file spec (non-wild). lxi d,fcb ;Give the address for the FCB. call comnd jmp kermit ;Give up on bad parse. call cfmcmd call getfil ;Open file. cpi 0FFH ;Succeed? jnz xmit1 lxi d,erms15 call prtstr ;Display error msg. jmp kermit xmit1: lxi d,inms19 ;Output start message. call prtstr call escpr ;Print the escape character. lxi d,inms20 ;Output 2nd part. call prtstr call escpr ;Print the escape character. lxi d,inms21 ;Print the rest. call prtstr mvi a,1 ;Start file I/O. sta fileio xra a ;Clear XOFF flag. sta xofflg ; fall through into xnext... ; ; assemble another line from the disk file. ; here from: previous page, rexmit xnext: call prtchr ; Copy characters from comm line to console mvi c,consta ; until user types anything on the console. call bdos ora a jz xnext ; nothing at console yet. lda eoflag ;EOF encountered? ora a jnz xend ;Yes, finish. xra a ;Reset line buffer counter. mov c,a sta cmaflg ;Reset carriage return flag. lxi d,cmdbuf ;Use comnd buffer as line buffer. lhld bufpnt ; Get current buffer pointer. lda chrcnt ; Get current byte count mov b,a ; in B xmit30: dcr b ; Assume there's a character there jp xmit2 ; If there was, proceed. call inbuf ; There wasn't. Try for another buffer. jmp xmit38 ; End of file. lhld bufpnt ; Got another buffer. Get new pointer in HL lda chrcnt ; and new byte count mov b,a ; in B xmit2: mov a,m ;Get a character from disk buffer. inx h ani 7FH ;Mask 7 bits. jz xmit30 ;Skip nulls. cpi cr ;Carriage return? jz xmit32 cpi subt ;CTRL-Z (substitute)? jz xmit37 cpi lf ;Line feed? jz xmit39 stax d ;Save to buffer. inx d lda cmaflg ;Carriage return seen? ora a jnz xmit31 ;Yes, don't count this character. inr c ;Count it. xmit31: jmp xmit30 ;Loop for next input byte. ; Carriage return seen. Start discarding characters until we see a line-feed. xmit32: sta cmaflg ;Mark return seen. jmp xmit30 ;And continue. ; Control-Z seen. Force end of file, and send the current line. xmit37: sta eoflag ;Mark EOF for next line. ; fall through... ; End of File encountered. eoflag has already been set; just send current line. xmit38: ; fall through... ; Linefeed seen. send the current line. xmit39: shld bufpnt ;Save next buffer pointer. mov a,b ;Save count of remaining characters. sta chrcnt mov a,c ;Save line length. sta filcnt ; fall through into rexmit... ; ; transmit the buffered line. ; here from: previous page, intchr rexmit: lda filcnt ;Set up line length. sta cmccnt lxi h,cmdbuf ;Set up line buffer pointer. shld cmcptr xmit40: call prtchr ;Receive comm. line & display. lda xofflg ;XOFF received? ora a jnz xmit40 ;Yes, wait for XON lda cmccnt ;Any characters left? dcr a jm xmit49 ;No, next state. sta cmccnt call selmdm ; select modem for outmdm lhld cmcptr ;Get the character to be sent mov a,m inx h ;Bump to next character. shld cmcptr call setpar ;Set parity (if any). mov e,a ;Save character (with parity) call outmdm ;Output it to the comm. line. ; TAC trap: If this character is the TAC intercept character, and the TAC ; trap is enabled, we have to output it twice. If the TAC trap is enabled, ; tacflg contains the intercept character. (The current character cannot ; be NUL, so we don't have to worry about doubling nulls in the message) lda tacflg ; get current intercept character, or zero. cmp m ; compare against current data character. jnz xmit41 ; if different, do nothing. call setpar ; match. set appropriate parity, mov e,a ; put it in the right register, call outmdm ; and output it a second time. xmit41: lda ecoflg ;Local echo? ora a jz xmit40 ;No, continue. mov a,e ;Get the character. ani 7FH ;Mask out parity. mov e,a ;Display on console. call selcon call outcon jmp xmit40 ;Continue. xmit49: xra a ;Reset last character seen. sta lstchr xmit50: call prtchr ;Receive comm. line & display. call conchr ;Read keyboard & send. jmp xendc ;CLOSE connection. lda lstchr ;Check last keyboard character. cpi cr ;Carriage return? jz xnext ;Yes, prepare to send next line. jmp xmit50 ;Continue, until carriage return. ; ; clean up. ; xend - end of file reached. close file, go to connect mode. ; here from: xnext. ; xendc - user wants out. close file, go to command mode. ; here from: rexmit. xend: call xmtoff ;Close file, etc. lxi d,inms22 ;Tell we're done with transmission jmp telnt1 ;Branch to CONNECT command. xendc: call xmtoff ;Close file, etc. jmp kermit ;Back to command loop. xmtoff: mvi c,closf ;Close file. lxi d,fcb call bdos xra a ;Terminate file I/O. sta fileio ret ; ; telnet - the CONNECT command. ; here from: kermit ; telnt1 - entry to connect mode from TRANSMIT command ; here from: xend telnet: call cfmcmd lxi d,infms7 ;Output start of message ; enter here from TRANSMIT command. telnt1: call prtstr call escpr ;Print the escape char. lxi d,infms8 ;Output some more of the message call prtstr call escpr ;Print the escape char again. lxi d,inms8a ;Print the remainder of the message call prtstr call syscon ;do system-dependent stuff lda logflg ;[pcc005] Want a log? ora a ;[pcc005] cnz logopn ;[pcc005] Open if so chrlup: call prtchr ;See if char at port (send to console). call conchr ;See if char at console (send to port). jmp kermit ;requested to end session - go to command loop. jmp chrlup ;Go do it again. ; ; ; prtchr - copy characters from comm line to console ; returns: nonskip, console selected. ; called by: xnext, rexmit, telnet ; prtchr: call selmdm ; select modem port call inpmdm ; try to get a character from it ora a ; test character jnz prtch0 ; if non-zero, process it. sta prtcnt ;[pcc008] zero out prt fairness count call selcon ; select console ret ; return. prtch0: ani 7FH ; drop parity bit. jz prtchr ; ignore null (filler) cpi del ; ignore delete, too jz prtchr cpi xon ;Is it an XON? jz prtxon ;yes cpi xoff ;Is it an XOFF? jz prtxof ;yes mov e,a ;Set the char aside. lda vtflg ;Get the VT52 emulation flag. cpi 1 ;Is the flag set? jnz prtch1 ;If not, don't do this stuff. lda escflg ;Get the escape flag. ora a ;Are we working on an escape sequence? jz prtch2 ;If not, continue. call vt52 ;If so, work on it some more jmp prtchr ;try for more characters. prtch2: mov a,e ;normal text. cpi esc ;Is the char an escape? jnz prtch1 ;If not skip on. mvi a,1 sta escflg ;Set the escape flag: escape seen. jmp prtchr ;Get another char... prtch1: call sysflt ; ok to print this character (in E)? ora a jz prtchr ; no, skip it. lda logflg ;Get the log flag. cpi 81H ;[pcc003] Are we logging cz logit ;[pcc003] Do so if needed call selcon ; select console lda prnflg ;Get Print parallel flag ora a cnz outlpt ; output to printer if flag set call outcon ; output to console. lxi h,prtcnt ;[pcc008] point to prt fairness count inr m ;[pcc008] bump mov a,m ;[pcc008] get it in a cpi prfair+1 ;[pcc008] time to be fair? jm prtchr ;[pcc008] no, go around again. mvi m,0 ;[pcc008] reset count ret ;[pcc008] and return ; I don't think we want to print xon/xoff - this should be ; flow control only across the link between us and the host. ; (besides, IBM host xon's don't make sense to most micros) ; remember xon/xoff state in xofflg (zero = xon, non-zero = xoff) prtxon: xra a ;Yes, reset XOFF flag prtxof: sta xofflg jmp prtchr ; look for another character ; ;[pcc005] Log file routines ;[pcc005] ; logopn - open the log file ; Open the log file and append to it if it already exists ; or create one if not. logopn: lxi h,lognam ;[pcc012] copy name lxi d,fcb ;[pcc012] to fcb lxi b,12 ;[pcc012] 12 bytes call mover ;[pcc012] copy it call appfil ;[pcc012] open file for appending jmp logerr ;[pcc012] error lxi h,logflg ;[pcc005] point to log flag mvi a,80H ;[pcc005] file open flag ora m ;[pcc005] or in contents of logflg mov m,a ;[pcc005] and store back lxi d,inms28 ;[pcc005] assume logging is on cpi 81H ;[pcc005] check jz prtstr ;[pcc005] print msg if true lxi d,inms27 ;[pcc005] no, must be suspended jmp prtstr ;[pcc005] print and return ; ; logit - output character in E to log file. ; we assume the host recognizes xon/xoff. (we probably shouldn't) ; modem port is selected. ; preserves de ; called by: prtchr logit: lxi h,chrcnt ;[pcc012] point to buffer count dcr m ;[pcc012] and decrement jp logit1 ;[pcc012] continue if ok push d ;[pcc012] save de call outadv ;[pcc012] advance buffer if in memory call logwrt ;[pcc012] sigh, time to write to disk pop d ;[pcc012] restore de lda logflg ;[pcc012] get logging flag ora a ;[pcc012] Did we quit because of an error rz ;[pcc012] return now if so logit1: lhld bufpnt ;[pcc012] get buffer pointer mov m,e ;Store the char. inx h shld bufpnt ret ;[pcc012] and return ;[pcc012] ; logwrt - write to log file with XON/XOFF since it may take a while. logwrt: mvi a,xoff ;^S to stop the host while we write the buffer. call setpar ; set correct parity... mov e,a call outmdm ; output it. call outbuf ;[pcc012] output the buffer and advance call logerr ;[pcc005] quit if error mvi a,xon ;^Q to restart the host call setpar ; set appropriate parity mov e,a call outmdm ; send it. ret ;[pcc012] ;[pcc005] ; logcls - Close the log file and reset the flag logcls: lxi d,infms6 ;[pcc005] Tell user we are closing file. call prtstr ;[pcc005] call clofil ;[pcc012] and do it jmp logerr ;[pcc005] jump if error lxi h,logflg ;[pcc005] point to flag mov a,m ;[pcc005] get it ani 7FH ;[pcc005] clear the open bit mov m,a ;[pcc005] and store back ret ;[pcc005] ;[pcc005] ; logerr - here on a variety of logging errors ; just close the file and disable logging ; called from logopn,logptr,logcls logerr: lxi d,erms22 ;[pcc005] Error message call prtstr ;[pcc005] print it mvi c,closf ;[pcc005] Close the file. lxi d,fcb ;[pcc012] call bdos ;[pcc005] xra a ;[pcc005] clear logflg sta logflg ;[pcc005] so don't try again ret ;[pcc005] ; ; ; VT52 emulation. ; called by: prtchr ; A/ contents of escflg (guaranteed non-zero) ; E/ current character ; modem is selected. ; vt52: cpi 1 ; first character after escape? jnz vt52y ; no, must be doing cursor positioning. ; ; E contains the character that followed the escape. ; valid characters are: ; A - cursor up ; B - cursor down ; C - cursor right ; D - cursor left ; F - enter graphics mode (hard to do on a non-vt52) ; G - exit graphics mode ; H - home ; I - reverse linefeed ; J - erase to end of screen ; K - erase to end of line ; Y - cursor positioning leadin ; Z - identify terminal as VT52 ; [ - enter hold-screen mode (not supported) ; \ - exit hold-screen mode (not supported) ; > - enter alternate-keypad mode? (not supported) ; = - exit alternate-keypad mode? (not supported) ; ; Invalid sequences are handled as the VT52 does - the escape and ; the following character are swallowed, never to be seen again. ; For E, the translation table may contain just '$' (no action), ; or may be used as clear-and-home, as in the Heath/Zenith H19. ; mov a,e ; get the second character of the sequence. cpi 'Y' ; if cursor lead-in handle it. jnz vt52a ; if not, go on. mvi a,2 ; state = 2: row follows. sta escflg ; update the flag. ret ; back for another character vt52a: cpi 'Z' ; VT52 ID query? jz vt52id ; yes. claim to be one. cpi 'A' ;Less than an 'A'? jm vtig ;Yes - ignore. cpi 'K'+1 ;Greater than 'K'? jp vtig ;Yes - ignore. sui 'A' ;Else make into index. rlc ;Multiply by four. rlc ;(Shift left twice.) lhld pttab ;Load base addr of table. mov e,a ;Move a into de pair. mvi d,00H ;Zero out high byte. dad d ;Double add index+offset. xchg ;Exchange de with hl. call selcon ; select console call prtstr ;and syscall. vtig: ;Ignore escape sequence. xra a ;Reset the ol' escape flag. sta escflg ret ;Return home. ; here for Z. Tell the host we're a VT52. (Sure we are...) vt52id: mvi a,esc ; response is escape... call setpar ; (need correct parity) mov e,a call outmdm ; (console already selected) mvi a,'/' ; ... slash ... call setpar ; (with parity) mov e,a call outmdm mvi a,'K' ; ... K. call setpar mov e,a call outmdm jmp vtig ; clear escape-sequence flag and return. ; here when escflg isn't 0 or 1 - processing cursor positioning sequence. vt52y: cpi 2 ; looking for row? (y-coordinate) jnz vt52x ; no, must be column. mov a,e ; yes. get coordinate sui (' '-1) ; convert from ascii (1 = top line) sta vtyval ; store for later mvi a,3 ; advance to next state (x coord) sta escflg ; store it ret ; try for another character ; here when escflag isn't 0, 1, or 2 - it must be 3. (right?) ; E holds the last character of the cursor positioning sequence. vt52x: xra a ; end of escape sequence, reset state. sta escflg mov a,e ; get column (' ' is left margin) sui (' '-1) ; make left margin be one mov c,a ; stash column in c lda vtyval ; get row number mov b,a ; in b call selcon ; select console call csrpos ; call system-dependent cursor positioner ret ; all through. ; ; ; conchr - copy character from console to comm line, processing ; (kermit's) escape sequences. ; Enter and exit with console selected. ; nonskip return: transparent mode terminated. ; skip return: still in transparent mode. ; called by: rexmit, telnet conchr: call inpcon ;Try to get a character from the console ani 07FH ;Keep only 7 bits jz rskp ;Null means nothing there. mov e,a ;Move the char for comparison. sta lstchr ;Save it lda escchr ;Get the escape char. cmp e ;Is it an escape char? jz intchr ;If so go process it. call selmdm ; select the modem mov a,e ;Get the char. call setpar ;Set parity (if any). mov e,a ;Restore it. call outmdm ;Output the char to the port. call selcon ; reselect console lda ecoflg ;Get the echo flag. ora a ;Is it turned on? jz rskp ;If not we're done here. mov a,e ;Get the char. ani 7FH ;Turn off the parity bit. mov e,a call outcon ; echo the character. jmp rskp ; use skip return ; ; transparent escape character has been typed. dispatch on second ; character. (console is still selected) ; here from: conchr intchr: call inpcon ; get another character from the console ora a ; zero means no character available yet. jz intchr ; If so, loop until we get a char. mov b,a ;Save the actual char. cpi ctrlc ;is it Control-C? jz contc ;yes ani 137O ;Convert to upper case. cpi 'C' ;Is it close? jnz intch0 ;If not proceed. contc: lxi d,infms9 ;Say we are back. call prtstr call syscls ; call system-dependent close routine lda logflg ;Get the log flag. ora a ;[pcc005] Check if open cm logcls ;[pcc005] Close if needed ret ;Here if not a 'C' or '^C' intch0: cpi 'S' ;Is it status? jnz inch01 ;If not, proceed. call stat01 ;Print out the status stuff. call prcrlf ;[pcc011] add a crlf jmp rskp ;return from conchr inch01: cpi 'R'-100O ;Control-R? jz inch02 ;Yes cpi 'R' ;(plain) R? jnz inch03 ;No inch02: lda fileio ;TRANSMIT in progress? ora a jz inch03 ;No,ignore pop b ;Remove return address (non-local goto) jmp rexmit ;Retransmit line inch03: mov a,b ;Get the char. cpi '?' ;Is it a help request? jnz intch1 ;If not, go to the next check. lda fileio ;TRANSMIT in progress? ora a jz inch3a ;[pcc003] No lxi d,xmthlp ;Tell about R too call prtstr inch3a: lda logflg ;[pcc003] Logging flag ora a ;[pcc003] see if active jp inch04 ;[pcc005] jump if no file open lxi d,loghlp ;[pcc003] yes, tell about R AND Q call prtstr ;[pcc003] inch04: lxi d,inthlp ;If so, get the address of the help message. call prtstr call sysinh ; print system-dependent help message lxi d,inhlp1 ; Tell about doubling the escape character call prtstr call escpr ;Print escape character lxi d,inhlp2 ;Print the rest call prtstr jmp intchr ;Get another char. intch1: mov a,b ;Get the character. cpi '0' ;Is it '0', to send a null? jnz intch3 ;No. xra a ;Yes, send an ASCII zero. call setpar ; with the correct parity mov e,a call selmdm ; (to the modem...) call outmdm call selcon ; return with console selected jmp rskp intch3: lda escchr ;Get the escape char. cmp b ;Is it the escape char? jnz intch4 ;[pcc002] jump if not mov a,b ;Get the char. call setpar mov e,a ;Restore it. call selmdm call outmdm ;Output it. call selcon ;We promised console would be selected... jmp rskp ;Return, we are done here. intch4: mov a,b ;[pcc002] get it again ani 137o ;[pcc002] in upper case cpi 'P' ;[pcc002] toggle printer? jnz intch5 ;[pcc003] nope lda prnflg ;[pcc002] get printer flag xri 01h ;[pcc002] complement it sta prnflg ;[pcc002] and put back jmp rskp ;[pcc002] intch5: lda logflg ;[pcc003] get log flag ora a ;[pcc003] See if open jp intch7 ;[pcc003] no, skip R and Q mov a,b ;[pcc003] get back chr ani 137o ;[pcc003] make upper case cpi 'R' ;[pcc003] Is it R jnz intch6 ;[pcc003] Jump if not mvi a,81H ;[pcc003] set flag for logging sta logflg ;[pcc003] put it back lxi d,inms28 ;[pcc003] message call prtstr ;[pcc003] jmp rskp ;[pcc003] done intch6: cpi 'Q' ;[pcc003] Quit logging? jnz intch7 ;[pcc003] no mvi a,82H ;[pcc003] flag for open, but suspended sta logflg ;[pcc003] store away lxi d,inms27 ;[pcc003] keep them informed call prtstr ;[pcc003] jmp rskp ;[pcc003] intch7: ;[pcc003] intchz: mov a,b ; not recognized. get saved copy back. call sysint ; interpret system-dependent sequences jmp rskp ; done. return (from conchr). mvi e,'G'-100O ;Otherwise send a beep. call outcon ; to the console. jmp rskp ; IF lasm LINK CP4CPM ENDIF;lasm