;V0.2 Brian Orr, 19 Dec 83. Added serial control port initialization routine ; to set RTS and DTR high as soon as program starts. ; ;V0.1 Bill Catchings, 2 Dec 83. Interrupt-driven i/o, snappy terminal ; emulation. GET command added. ; ;V0.0 Bill Catchings, 20 Oct 83. Preliminary version, polled i/o, sluggish ; terminal emulation. No GET. ; ; KERMIT for the DEC Rainbow-100, CP/M-86 v1.0 or v2.0. ; ; Copyright (C) 1983 William B. Catchings III ; ; This program implements the Kermit Protocol developed at Columbia ; University. This version is being written specifically for the ; DEC Rainbow 100. It will hopefully take advantage of much of what ; was learned in implementing the CP/M-80, DEC-20 and other versions. ; ; Things to do before declaring this to be "version 1": ; ; . Add wild card sends. ; . Add SET FILE ASCII/BINARY command, don't always send full blocks. ; . If incoming filename illegal, make it legal, don't die. ; In particular, watch out for names longer than 8.3. ; . Port input buffer should be cleared before sending each packet. ; ; Things to do after that: ; ; . Add new features from Kermit-80, like ERA & DEL commands. ; . Make NOUT decimal rather than HEX. TITLE 'Kermit' ; Definitions: soh EQU 01O bell EQU 07O tab EQU 11O lf EQU 12O ff EQU 14O cr EQU 15O xon EQU 21O xoff EQU 23O esc EQU 33O del EQU 177O parevn EQU 00H ; Even parity. parmrk EQU 01H ; Mark parity. parnon EQU 02H ; No parity. parodd EQU 03H ; Odd parity. parspc EQU 04H ; Space parity. defpar EQU parnon ; Default parity (none). ibmpar EQU parmrk ; IBM's parity (mark). defesc EQU '\'-100O ; Control backslash by default. diasw EQU 00H ; Default is diagnostics off. ; The actual program: CSEG ; Start coding! pushf ; Push flags on CCP's stack. pop ax ; Save flags in AX. mov bx, ds ; Set SS to DS. mov ss, bx lea sp, stack ; Set up the stack pointer. push ax ; Restore the flags. popf lea dx, versio ; Print the version header. call tcmsgc call serin3 ; serial control port init to set RTS & DTR ; This is the main KERMIT loop. It prompts for and gets the users commands. kermit: lea dx, kerm call prompt ; Prompt the user. lea dx, comtab lea bx, tophlp mov ah, cmkey call comnd jmp kermt2 ; Tell them about the error. call bx ; Call the routine returned. jmp kermt3 ; Tell them about the error. cmp extflg, 0 ; Check if the exit flag is set. je kermit ; If not, do it again. call haltf ; If so, exit. ret ; Just in case! kermt2: lea dx, ermes1 ; Give an error. call tcrmsg jmp kermit kermt3: lea dx, ermes3 ; Give an error. call tcrmsg jmp kermit ; This is the EXIT command. It leaves KERMIT and returns to CP/M. exit: mov ah, cmcfm call comnd ; Get a confirm. jmp r mov extflg, 1 ; Set the exit flag. jmp rskp ; This is the HELP command. help: mov ah, cmcfm call comnd ; Get a confirm. jmp r lea dx, tophlp ; Print some help. call tmsg jmp rskp ; FINISH - tell remote KERSRV to exit. finish: mov ah,cmcfm ; Parse a confirm. call comnd jmp r mov ah, 'F' call gensen ; Send the finish command. jmp rskp ; BYE - tell remote KERSRV to logout and then exit to CP/M. bye: mov ah,cmcfm ; Parse a confirm. call comnd jmp r mov ah, 'L' call gensen ; Send the logout command. mov extflg,1 ; Set exit flag. jmp rskp ; LOGOUT- tell remote KERSRV to logout. logout: mov ah,cmcfm ; Parse a confirm. call comnd jmp r mov ah, 'L' call gensen ; Send the logout command. jmp rskp ; RECEIVE - Receive a file or files from the remote Kermit. For compati- ; bility the receive (now GET is the command.) rec: mov ah, cmtxt ; Parse an arbitrary text string. lea bx, data ; Where to put the parsed text. call comnd jmp r cmp ah, 0 ; Read in any chars? jne get1 ; If so, join the get code. call read ; Do the actual protocol work. jmp rskp ; GET - Get a file or files from the remote server Kermit. get: mov ah, cmtxt ; Parse an arbitrary text string. lea bx, data ; Where to put the parsed text. call comnd jmp r cmp ah, 0 ; Read in any chars? jne get1 ; If not give an error. lea dx, ermes5 call tcrmsg jmp rskp get1: mov al, ah mov ah, 0 mov argbk1, ax ; Remember number of chars we read. mov ah, '$' ; Used for printing. mov [bx], ah call init ; Clear line and initialize buffers. call clrfln ; Prepare to print filename. lea dx, data ; Print file name. call tmsg mov argblk, 0 ; Start at packet zero. mov ah, 'R' ; Receive init packet. call spack ; Send the packet. jmp r call read1 ; Join the read code. jmp rskp ; SEND - Send a file or files to the remote Kermit. sencom: mov ah, cmifi ; Parse an input file spec. lea dx, fcb ; Give the address for the FCB. call comnd jmp r ; Give up on bad parse. mov ah, cmcfm call comnd ; Get a confirm. jmp r ; Didn't get a confirm. call send jmp rskp ; SET - Set some Kermit parameter. setcom: lea dx, settab ; Parse a keyword from the set table. lea bx, sethlp mov ah,cmkey call comnd jmp r call bx ; Call the specific set routine. jmp rskp ; This is the ESCAPE character SET subcommand. escape: call cmgtch ; Get a char. cmp ah, 0 jns es1 ; Terminator or no? and ah, 7FH ; Turn off minus bit. cmp ah, '?' jne es0 lea dx, eschlp call tmsgcr lea dx, kerm call tmsg mov bx, cmdptr mov al, '$' mov [bx], al lea dx, cmdbuf call tmsg dec cmcptr ; Ignore dollar sign. dec cmccnt mov cmaflg, 0 jmp repars es0: lea dx, ermes3 call tmsg ret es1: mov temp, ax call cmcfrm jmp es0 mov ax, temp mov escchr, ah ; Save new value. ret ; This is the End-of-line character SET subcommand. eolset: call cmgtch ; Get the first char into AH. cmp ah, 0 jns eol1 cmp ah, 0BFH ; Question mark? je eol4 jmp eol3 eol1: sub ah, 030H ; Digit --> real number. mov temp, ax ; Save the input. call cmcfrm jmp eol0 ; Got a char. mov ax, 0 mov bx, temp jmp eol2 eol0: sub ah, 030H ; Digit --> real number. mov temp1, ax call cmcfrm jmp eol3 ; Too many chars. mov bx, temp1 mov ax, temp xchg al, ah mov ah, 0 eol2: mov temp, ax ; Save our registers. mov temp1, bx call cmcfrm jmp eol3 mov bx, temp1 mov ax, temp mov temp2, 10 ; Get numerical value of char. mul temp2 add al, bh mov ah, 0 cmp al, 0 jl eol3 cmp al, 1FH jg eol3 mov seol, al ; Use new eol char. ret eol3: lea dx, eolerr ; Bad end-of-line char. call tmsg jmp prserr eol4: lea dx, eolhlp call tmsg lea dx, kerm call tcrmsg mov bx, cmdptr mov al, '$' mov [bx], al lea dx, cmdbuf call tmsg dec cmcptr ; Don't count the dollar sign. dec cmccnt ; Or the question mark. mov cmaflg, 0 ; Check for more input. jmp repars ; This is the LOCAL echo SET subcommand. local: lea dx, ontab lea bx, onhlp mov ah, cmkey call comnd jmp r push bx ; Save the parsed value. mov ah, cmcfm call comnd ; Get a confirm. jmp r ; Didn't get a confirm. pop bx mov ecoflg, bl ; Set the local echo flag. ret ; This is the SET IBM command. ibmset: lea dx, ontab lea bx, onhlp mov ah, cmkey call comnd jmp r push bx mov ah, cmcfm call comnd ; Get a confirm. jmp r ; Didn't get a confirm. pop bx mov ibmflg, bl ; Set the IBM flag. cmp bl, 0 ; Turning on or off? je ibmst1 ; If off, set parity & local echo to defaults. mov ah, ibmpar ; Set IBM parity. mov al, 1 ; Set local echo on. jmp ibmst2 ibmst1: mov ah, defpar ; Reset parity to default. mov al, 0 ; Turn off local echo. ibmst2: mov parflg, ah ; Set parity. mov ecoflg, al ; And local echo. ret ; This is the SET File-Warning command. filwar: lea dx, ontab lea bx, onhlp mov ah, cmkey call comnd jmp r push bx mov ah, cmcfm call comnd ; Get a confirm. jmp r ; Didn't get a confirm. pop bx mov flwflg, bl ; Set the file warning flag. ret ; This is the SET Parity command. setpar: lea dx, partab lea bx, parhlp mov ah, cmkey call comnd jmp r push bx mov ah, cmcfm call comnd ; Get a confirm. jmp r ; Didn't get a confirm. pop bx mov parflg, bl ; Set the parity flag. ret ; Sets debugging mode on and off. debset: lea dx, ontab lea bx, onhlp mov ah, cmkey call comnd jmp r push bx mov ah, cmcfm call comnd ; Get a confirm. jmp r ; Didn't get a confirm. pop bx mov debug, bl ; Set the debug flag. ret ; STATUS - Give some statistics on the connection. (NYI) status: mov ah,cmcfm ; Parse a confirm. call comnd jmp r lea dx, infms6 ; Tell the user that it's not yet implemented. call tcrmsg jmp rskp ; This is the CONNECT command. It makes the local Kermit act as a terminal. telnet: mov ah,cmcfm call comnd ; Get a confirm. jmp r ; Didn't get a confirm. lea dx, inms01 call tcrmsg call escprt lea dx, inms02 call tmsgcr call serini ; Initialize serial port. telnt1: call conchr ; Check keyboard. jmp extln ; Exit from Telnet. telnt2: call prtchr ; Check serial port. jmp telnt1 ; No char, just loop. mov dl, al ; Get the character. call dbout jmp telnt2 ; Get next character from serial port. extln: lea dx, inms03 call tcmsgc jmp rskp prtchr: call instat ; Any characters available? jmp r ; No, just return call inchr ; Get a character. and al, 7FH ; Strip off parity bit. jmp rskp conchr: call dbinst ; Any chars out there? jmp rskp ; If not, give skip return. call dbin ; Yes, then get the char. cmp al, escchr ; Is it an escape char? jz intchr ; If so go process it. call dopar ; Set parity (if any). mov dx, ax call prtout ; Output the char to the port. cmp ecoflg, 0 ; Is the echo flag turned on? jnz cnc1x jmp rskp ; If not we're done here. cnc1x: call dbout ; Echo the character. jmp rskp intchr: call dbin ; Get a char. cmp al, 0 ; Is the char a null? jz intchr ; If so, go until we get a char. mov bl, al ; Save the actual char. and al, 137O ; Convert to upper case. cmp al, 'C' ; Is it close? jne intch0 ret intch0: cmp bl, escchr ; Is it the escape char? jne intch2 ; If not, go send a beep to the user. call dopar ; Set parity. intc11: mov al, bl call prtout ; Output it. jmp rskp ; Return, we are done here. intch2: mov dl, bell ; Otherwise send a beep. call dbout jmp rskp ; This routine prints out the escape character in readable format. escprt: mov dl, escchr cmp dl, ' ' jge escpr2 lea dx, esctl ; Print "Control-" call tmsg mov dl, escchr add dl, 040H ; Make it printable. escpr2: call bout ret ; Set parity for character in Register AL. dopar: cmp parflg, parnon ; No parity? je parret ; Just return cmp parflg, parevn ; Even parity? jne dopar0 and al, 7FH ; Strip parity. jpe parret ; Already even, leave it. or al, 80H ; Make it even parity. jmp parret dopar0: cmp parflg, parmrk ; Mark parity? jne dopar1 or al, 80H ; Turn on the parity bit. jmp parret dopar1: cmp parflg, parodd ; Odd parity? jne dopar2 and al, 7FH ; Strip parity. jpo parret ; Already odd, leave it. or al, 80H ; Make it odd parity. jmp parret dopar2: and al, 7FH ; Space parity - turn off parity bit. parret: ret ; SHOW - Show the state of the parameters available from the SET command. show: mov ah,cmcfm ; Parse a confirm. call comnd jmp r lea dx, cmcrlf call tmsg lea dx, locst ; Get the address of the local echo string. call tcrmsg cmp ecoflg, 0 ; Is the local echo flag on? call ponoff ; Print on or off. lea dx, ibmst ; IBM string. call tcrmsg cmp ibmflg, 0 ; Is the IBM flag on? call ponoff lea dx, filst ; File warning string. call tcrmsg cmp flwflg, 0 ; Is the file warning flag on? call ponoff lea dx, debst ; Debugging string. call tcrmsg cmp debug, 0 ; Is the debugging flag on? call ponoff lea dx, parst ; Parity string. call tcrmsg lea dx, pnonst ; None by default. cmp parflg, parmrk ; Is it mark? jnz show5 lea dx, pmrkst ; Say mark. jmp show55 show5: cmp parflg, parspc ; Is it space? jnz show51 lea dx, pspcst ; Say space. jmp show55 show51: cmp parflg, parodd ; Is it odd? jnz show52 lea dx, poddst ; Say odd. jmp show55 show52: cmp parflg, parevn ; Is it even? jnz show55 lea dx, pevnst ; Say even. show55: call tmsg ;* Print the EOL char. lea dx, escst ; Escape string. call tcrmsg call escprt ; Print the escape char. lea dx, crlf ; Get the address of a crlf. call tmsg jmp rskp ponoff: jz ponof2 ; If not say so. lea dx, onstr ; Say on. jmp ponof3 ponof2: lea dx, offstr ; Say off. ponof3: call tmsg ret DSEG ; Data segment. ORG 100H ; Leave room for system page. RW 80 ; Eighty word or so stack. stack RW 2 ; Impure storage extflg db 0 ; Exit flag; non zero to exit. ecoflg db 0 ; Echo flag; non zero for local echoing. escchr db defesc ; Escape character for the connect command. debug db diasw ; Are we in debug mode? ; COMND tables comtab db 0CH ; Twelve entries. db 03H,'BYE$' dw bye db 07H,'CONNECT$' dw telnet db 04H,'EXIT$' dw exit db 06H,'FINISH$' dw finish db 03H,'GET$' dw get db 04H,'HELP$' dw help db 06H,'LOGOUT$' dw logout db 07H,'RECEIVE$' dw rec db 04H,'SEND$' dw sencom db 03H,'SET$' dw setcom db 04H,'SHOW$' dw show db 06H,'STATUS$' dw status settab db 07H ; Seven db 05H,'DEBUG$' dw debset db 0BH,'END-OF-LINE$' dw eolset db 06H,'ESCAPE$' dw escape db 0CH,'FILE-WARNING$' dw filwar db 03H,'IBM$' dw ibmset db 0AH,'LOCAL-ECHO$' dw local db 06H,'PARITY$' dw setpar ; db 07H,'TIMEOUT$' ; dw timset ontab db 02H ; Two entries. db 02H,'ON$' dw 01H db 03H,'OFF$' dw 00H yestab db 02H ; Two entries. db 02H,'NO$' dw 00H db 03H,'YES$' dw 01H partab db 05H ; Five entries. db 04H,'EVEN$' dw parevn db 04H,'MARK$' dw parmrk db 04H,'NONE$' dw parnon db 03H,'ODD$' dw parodd db 05H,'SPACE$' dw parspc ; Pure storage. delstr db ' ',10O,10O,'$' ; Delete string. clrspc db ' ',10O,'$' ; Clear space. clrlin db cr,esc,'[K$' ; Clear line. clreol db esc,'[K$' ; Clear to end of line. clrscr db esc,'[H',esc,'[J$' ; Home and clear screen. homcur db esc,'[H$' ; Home cursor. kerm db 'Kermit-86>$' spmes db 'Spack: $' rpmes db 'Rpack: $' hibit db 'Warning - Non Ascii char$' foo db '$' ender db bell,bell,'$' inms01 db '[Connecting to host, type $' inms02 db ' C to return to PC]$' inms03 db '[Back at micro]$' infms3 db 'Completed $' infms4 db 'Failed $' infms5 db 'Renaming file to $' infms6 db '%Function not implemented$' ermes1 db '?Unregonized command$' ermes2 db '?Illegal character$' ermes3 db '?Not confirmed$' ermes4 db 'Unable to rename file$' ermes5 db '?No receive file specification given$' 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$' erms11 db '?Disk full$' erms14 db '?Unable to receive an acknowledgement from the host$' erms15 db '?Unable to find file$' erms17 db 'Record length exceeds size of buffer$' erms18 db '?Unable to tell host that session is finished$' erms19 db '?Unable to tell host to logout$' cfrmes db ' Confirm with carriage return $' filhlp db ' Input file spec (possibly wild) $' esctl db 'Control-$' spchar db 24H,26H,23H,40H,21H,25H,27H,28H,29H,2DH db 3CH,3EH,7BH,7DH,5FH,5CH,5EH,7EH,7CH,60H eschlp db cr,lf,'Enter literal value (ex: Cntrl ]) $' tophlp db cr,lf,'BYE to host (LOGOUT) and exit to CP/M' db cr,lf,'CONNECT to host on selected port' db cr,lf,'EXIT to CP/M' db cr,lf,'FINISH running Kermit on the host' db cr,lf,'GET file(s) from host' db cr,lf,'HELP by giving this message' db cr,lf,'LOGOUT the host' db cr,lf,'RECEIVE file(s) from host' db cr,lf,'SEND file(s) to host' db cr,lf,'SET a parameter' db cr,lf,'SHOW the parameters' db cr,lf,'STATUS of Kermit (NYI)$' sethlp db cr,lf,'DEBUG' db cr,lf,'END-OF-LINE character' db cr,lf,'ESCAPE character change' db cr,lf,'FILE-WARNING' db cr,lf,'IBM' db cr,lf,'LOCAL-ECHO echoing (half-duplex)' db cr,lf,'PARITY type' db cr,lf,'TIMEOUT value (NYI)$' onhlp db cr,lf,'OFF ON$' yeshlp db cr,lf,'NO YES$' parhlp db cr,lf,'NONE MARK ODD EVEN SPACE$' eolhlp db cr,lf,'Decimal digit between 0 and 31$' eolerr db cr,lf,'Illegal end-of-line character$' timhlp db cr,lf,'Decimal digit between 1 and 94$' timerr db cr,lf,'Illegal timeout value$' inthlp db cr,lf,'? This message' db cr,lf,'C Close the connection' ; db cr,lf,'S Status of the connection' db cr,lf,'Typing the escape character will send it to the host' db cr,lf,cr,lf,'Command>$' onstr db ' is on$' offstr db ' is off$' locst db 'Local echo$' ibmst db 'IBM flag$' filst db 'File warning$' debst db 'Debug mode$' escst db 'Escape char: $' parst db 'Parity: $' pnonst db 'none$' pmrkst db 'mark$' pspcst db 'space$' poddst db 'odd$' pevnst db 'even$' eolst db 'End-of-line character is ^$' timmes db 'Timeout is $' outlin db esc,3CH,esc,'[H',esc,'[J',cr,lf,tab,tab versio db 'Rainbow-100 CP/M-86 Kermit-86 - V0.2',cr,lf,'$' outln2 db cr,lf,'Number of packets: ' db cr,lf,'Number of retries: ' db cr,lf,'File name: $' ; Cursor addressing items. scrnp db esc,'[4;22H$' ; Place for number of packets. scrnrt db esc,'[5;22H$' ; Place for number of retries. scrfln db esc,'[6;12H',esc,'[K$' ; Place for file name. screrr db esc,'[12;1H$' ; Place for error messages. scrst db esc,'[4;53H$' ; Place for "Complete". scrrpr db esc,'[13;1H$' ; Place for prompt. ;scrhi db esc,'[5;53H$' ; 8th bit on in character. scrfr db esc,'[6;1H$' ; Rename file. scrsp db esc,'[8;1H$' ; Send packet. scrrp db esc,'[10;1H$' ; Receive packet. ESEG ; Extra segment. INCLUDE KERIO.A86 ; Get the I/O routines. INCLUDE KERFIL.A86 ; Get the file routines. INCLUDE KERUTL.A86 ; Get the utility routines. INCLUDE KERCMD.A86 ; Get the COMND routines. INCLUDE KERPRO.A86 ; Get the protocol routines. DSEG $ ; Resume data segment. DB 0 ; Is this really necessary? END