home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Oakland CPM Archive
/
oakcpm.iso
/
cpm
/
modem
/
cp405src.ark
/
CP4TT.ASM
< prev
next >
Wrap
Assembly Source File
|
1986-12-25
|
23KB
|
693 lines
; 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 <esc>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 <esc>Q to suspend logging and <esc>R to
; resume. <esc>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 <esc>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 <esc>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