.title k11pak packet driver for kermit-11 .ident /8.0.01/ .enabl gbl ; Brian Nelson 30-Nov-83 10:20:09 ; Last edit: 02-Jul-85 14:44:32 ; ; Change Software, Toledo, Ohio ; University of Toledo, Toledo, Ohio ; .enabl lc ; define macros and things we want for KERMIT-11 ; ; K11MAC.MAC defines all macros and a number of symbols .include /IN:K11DEF.MAC/ .if ndf, K11INC .ift .include /IN:K11MAC.MAC/ .endc .iif ndf, k11inc, .error ; INCLUDE for IN:K11MAC.MAC failed .include /IN:K11DEF.MAC/ maxpak == 94. ; maximum packet size-maxsize(checksum) mx$try == 10 ; number of times to retry packet myquote == '# ; quoting mypad == 0 ; no padding mypchar == 0 ; thus no pad character myeol == cr ; end-of-line mytime == 12 ; time me out after this myqbin == '& ; 8 bit quoting defchk == '1 mychkt == defchk ; normal checksumming myrept == 176 ; tilde for repeat things mycapa == capa.a+capa.l ; /42/ Attributes + long packets maxtim == 60 ; maximum timeout mintim == 2 ; minimum timeout badchk == 377 ; psuedo packet type for checksum timout == 'T&137 ; psuedo packet type for timeout defdly == 6 ; delay for SENDING to start up .sbttl notes on RMS-11 ; RSTS and RSX note: ; ; Note that we really don't need distinct luns for input, output ; and directory lookup as we would normally never have more than ; one of them active at any given time. The space used to do this ; only adds about 1 KW of size to the task so I am not going to ; worry about it. There could always come a time when the above ; assumption will not hold. Most of KERMIT-11 is sharable anyway ; due to the linking to RMSRES. The code, all being in PSECT $CODE ; can always be task built with the /MU switch to make more of it ; sharable (RSTS and RSX11M Plus only). ; The one thing to note is that LUN.LO must ALWAYS be reserved as ; logging and debugging to disk can be running concurrently with ; anything else. Also, when the TAKE command is put in another lun ; will be required for it. lun.kb == 0 ; assume if channel 0 --> terminal lun.in == 1 ; channel for input files lun.ou == 2 ; channel for output files lun.lo == 3 ; channel for packet and file logging lun.tr == 3 ; same as lun.log lun.ta == 4 ; for the TAKE command lun.tt == 5 ; for RSX, the normal TI: channel lun.sr == 6 ; channel for $search for RMSv2.0 lun.ti == 7 ; channel number for connected terminal lun.xk == 7 ; Ditto, for clarity lun.co == 10 ; used as is lin.ti for remote connect lun.as == 11 ; used to attach to remote link device ; to fake a device assignment .psect $pdata null: .byte 0,0 ; a null packet to send .psect $code .sbttl KERMIT packet format ; PACKET FORMAT ; ;The KERMIT protocol is built around exchange of packets of the following for- ;mat: ; ; +------+-----------+-----------+------+------------+-------+ ; ] MARK ] char(LEN) ] char(SEQ) ] TYPE ] DATA ] CHECK ] ; +------+-----------+-----------+------+------------+-------+ ; ;where all fields consist of ASCII characters. The fields are: ; ;MARK The synchronization character that marks the beginning of the packet. ; This should normally be CTRL-A, but may be redefined. ; ;LEN The number of ASCII characters within the packet that follow this ; field, in other words the packet length minus two. Since this number ; is transformed to a single character via the char() function, packet ; character counts of 0 to 94 (decimal) are permitted, and 96 (decimal) ; is the maximum total packet length. The length does not include end- ; of-line or padding characters, which are outside the packet and are ; strictly for the benefit of the operating system, but it does include ; the block check characters. ; ;SEQ The packet sequence number, modulo 64, ranging from 0 to 63. Sequence ; numbers "wrap around" to 0 after each group of 64 packets. ; ; ;TYPE The packet type, a single ASCII character. The following packet types ; are required: ; ; D Data packet ; Y Acknowledge (ACK) ; N Negative acknowledge (NAK) ; S Send initiate (exchange parameters) ; B Break transmission (EOT) ; F File header ; Z End of file (EOF) ; E Error ; ; ;DATA The "contents" of the packet, if any contents are required in the given ; type of packet, interpreted according to the packet type. Control ; characters are preceded by a special prefix character, normally "#", ; and "uncontrollified" via ctl(). A prefixed sequence may not be broken ; across packets. Logical records in printable files are delimited with ; CRLFs, suitably prefixed (e.g. "#M#J"). Any prefix characters are in- ; cluded in the count. Optional encoding for 8-bit data and repeated ; characters is described later. ; ; ;CHECK A block check on the characters in the packet between, but not includ- ; ing, the mark and the block check itself. The check for each packet is ; computed by both hosts, and must agree if a packet is to be accepted. ; A single-character arithmetic checksum is the normal and required block ; check. Only six bits of the arithmetic sum are included. In order ; that all the bits of each data character contribute to this quantity, ; bits 6 and 7 of the final value are added to the quantity formed by ; bits 0-5. Thus if s is the arithmetic sum of the ASCII characters, ; then ; ; check = char((s + ((s AND 192)/64)) AND 63) ; ; This is the default block check, and all Kermits must be capable of ; performing it. Other optional block check types are described later. ; The block check is based on the ASCII values of the characters in the ; packet. Non-ASCII systems must translate to ASCII before performing ; the block check calculation. ; ; ; ; 13-Oct-84 14:01:32 BDN moved SENDSW and RECSW out .sbttl GETCR0 decide where to get the next character from ; 06-Nov-85 11:22:14 BDN Added Edit 38 ; ; Passed: r0 LUN ; Return: r0 Error code (generally 0 or ER$EOF) ; r1 Character just read ; ; ; GETCR0 is the lowest level entry point called in Kermit to ; obtain the next character for a SEND function (even GETC ; calls it), where that it may be a normal file transfer, or ; a SERVER extended response. The main idea in altering it is ; so that a server dispatch routine can change the the ; default (get from a file) to, say, get from an .ASCIZ ; string in memory or switch to some other kind of ; GET_NEXT_CHARACTER routine. This requires that the service ; routine insert its GET_NEXT_CHAR routine address into the ; global 'GETCROUTINE' and also to reset it to 'FGETCR0' when ; the action is complete. Currenty, REMOTE HELP and REMOTE ; DIR use this facility. getcr0::tst getcroutine ; /38/is there any routine address set bne 10$ ; /38/yes call fgetcr0 ; /38/no, default to file reading br 100$ ; /38/exit 10$: call @getcroutine ; /38/call currently defined routine 100$: return tgetcr::tst tgetaddr ; /38/Have we ever been inited ? beq 90$ ; /38/no, return ER$EOF movb @tgetaddr,r1 ; /38/yes, get next character please beq 90$ ; /38/nothing is left to do inc tgetaddr ; /38/text_address++ clr r0 ; /38/return(no_errors) br 100$ ; /38/exit 90$: mov #ER$EOF ,r0 ; /38/return(end_of_file) mov #fgetcr0,getcroutine ; /38/reset to file reading please 100$: return ; /38/exit global .sbttl spack send packet ; S P A C K $ ; ; spack$(%val type,%val num,%val len, %loc data) ; ; input: @r5 type of packet ; 2(r5) packet number ; 4(r5) length of the packet ; 6(r5) location of the data to send ; output: r0 error status $ALLSIZ = >&177776 spack$::save ; Save registers that we may use call spakwa call spakin sub #$ALLSIZ,sp ; /42/ Allocate a LONG buffer mov sp ,r4 ; Point to the buffer clr -(sp) ; Count the total length tst prexon ; /53/ Should we prefix all packets beq 5$ ; /53/ with an XON? If eq, NO movb #'Q&37 ,(r4)+ ; /53/ Yes, insert one inc @sp ; /53/ Write_length++ 5$: setpar sensop ,(r4)+ ; Start all packets with control A mov r4 ,r2 ; Get address for checksum compute inc @sp ; Packetlength := succ(packetlength) mov 4(r5) ,r0 ; The length of the packet cmp r0 ,#MAXPAK ; Packet too large ? blos 15$ ; No bitb #CAPA.L,conpar+p.capas ; /43/ Check to see if both sides beq 10$ ; /43/ REALLY understand long packets bitb #CAPA.L,senpar+p.capas ; /43/ We would normally but it is beq 10$ ; /43/ possible to SET NOLONG tst senlng ; /42/ Receiver said it can do long beq 10$ ; /42/ packets? If eq, then no ; /42/ Otherwise, build ext header. mov r2 ,-(sp) ; /42/ Save this mov #40 ,-(sp) ; /42/ Accumulate header checksum setpar #40 ,(r4)+ ; /42/ Length is a space, of course. tochar 2(r5) ,r1 ; /42/ Packet sequence please add r1 ,(sp) ; /42/ Add into header checksum now. setpar r1 ,(r4)+ ; /42/ Insert it movb (r5) ,r1 ; /42/ The packet type is next. bicb #40 ,r1 ; /42/ Insure always upper case. add r1 ,(sp) ; /42/ Add in the checksum setpar r1 ,(r4)+ ; /42/ And insert that also mov r0 ,r3 ; /42/ Insert the total packet size clr r2 ; /42/ First byte is size/95. add chksiz ,r3 ; /42/ Must include checksum size. div #95. ,r2 ; /42/ Second byte is size mod 95 tochar r2 ,r2 ; /42/ Convert to character rep tochar r3 ,r3 ; /42/ Convert to character rep setpar r2 ,(r4)+ ; /42/ Insert high bits into packet add r2 ,(sp) ; /42/ Add into checksum setpar r3 ,(r4)+ ; /42/ Insert low bits into packet add r3 ,(sp) ; /42/ Add into checksum mov (sp)+ ,r0 ; /42/ Pop the checksum please mov r0 ,r2 ; /42/ Save it bic #^C300 ,r2 ; /42/ Compute it as in: ash #-6 ,r2 ; /42/ Chk=char((s+((s&0300)/0100))&77) add r0 ,r2 ; /42/ ... bic #^C77 ,r2 ; /42/ Got it now tochar r2 ,r2 ; /42/ Convert checksum to character setpar r2 ,(r4)+ ; /42/ and insert into packet. mov (sp)+ ,r2 ; /42/ Where to start checksum for rest mov #7 ,(sp) ; /42/ We now have seven characters. br 20$ ; /42/ Add off we go 10$: mov #MAXPAK-3,r0 ; Yes, reset packet size please 15$: add #2 ,r0 ; + two for number and type add chksiz ,r0 ; + the length of the checksum please clr r1 ; Accumulated checksum tochar r0 ,r1 ; Start the checksum out right setpar r1 ,(r4)+ ; And stuff length into the packet inc @sp ; Packetlength := succ(packetlength) tochar 2(r5) ,r0 ; Convert the packet number now setpar r0 ,(r4)+ ; And stuff it into the packet inc @sp ; Packetlength := succ(packetlength) movb @r5 ,r0 ; Get the packet type now bicb #40 ,r0 ; Insure UPPER CASE packet type setpar r0 ,(r4)+ ; Insert the packet type into buffer inc @sp ; Packetlength := succ(packetlength) 20$: mov 4(r5) ,r1 ; Get the data length beq 40$ ; Nothing to do mov 6(r5) ,r3 ; Address of the data to send 30$: clr r0 ; Get the next character bisb (r3)+ ,r0 ; Next char setpar r0 ,(r4)+ ; Now move the data byte into the buffer inc @sp ; Packetlength := succ(packetlength) sob r1 ,30$ ; Next please 40$: clrb @r4 ; Set .asciz for call to checks mov r2 ,-(sp) ; Starting address for checksum field call checks ; Simple mov (sp)+ ,r2 ; Get the computed checksum now call spakck ; Stuff checksum into buffer now add r0 ,@sp ; And the length of the checksum setpar conpar+p.eol,(r4)+ ; End of line needed ? inc @sp ; Packetlength := succ(packetlength) mov (sp)+ ,r1 ; Packet length mov sp ,r4 ; Address(buffer) calls pakwri ,; And dump the buffer out now call spakfi ; Handle ibm stuff if possible add #$ALLSIZ,sp ; Pop the buffer unsave ; Pop registers that we used return GLOBAL GLOBAL ; /53/ .sbttl spack routines .enabl lsb spakin::bit #log$pa ,trace ; tracing today ? beq 5$ ; no calls dskdmp ,<#200$,4(r5),@r5,2(r5),6(r5)> 5$: tst pauset ; wait a moment ? beq 6$ ; no calls suspend , ; yes 6$: mov #conpar+p.padc,r2 ; address of the pad character ? clr r1 bisb conpar+p.npad,r1 ; send some pad characters ? tst r1 beq 20$ ; no padding 10$: calls pakwri ,; send some padding sob r1 ,10$ ; next please 20$: movb @r5 ,r1 ; the packet type next cmpb r1 ,#'A&137 ; a legitimate packet type ? blo 30$ ; no cmpb r1 ,#'Z&137 ; must be in the range A..Z bhi 30$ ; no good sub #100 ,r1 ; convert into range 1..26 asl r1 ; and count the packet type asl r1 ; /43/ 32 bits add #1 ,pcnt.s+2(r1) ; /43/ 32 bits, paccnt(type)++ adc pcnt.s+0(r1) ; /43/ 32 bits, the high part add #1 ,pcnt.s+2 ; /43/ 32 bits now adc pcnt.s+0 ; /43/ The high order part 30$: return .save .psect $PDATA ,D 200$: .asciz /SPACK - / .even .restore .dsabl lsb spakck: clr r0 ; checksum.len := 0 cmpb chktyp ,#defchk ; if checklength > 6 bits blos 20$ ; then begin cmpb chktyp ,#'3 ; if checktype = CRC16 bne 10$ ; then begin mov r2 ,r1 ; checkchar1:=tochar(check[12..15]) ash #-14 ,r1 ; shift over 12 bits bic #^C17 ,r1 ; mask off the high 12 bits tochar r1 ,@r4 setpar @r4 ,(r4)+ inc r0 ; packetlength := succ(packetlength) ; end 10$: mov r2 ,r1 ; checkchar1 := tochar(check[6..11]) ash #-6 ,r1 ; shift over 6 bits bic #^C77 ,r1 ; mask off the higher order bits tochar r1 ,@r4 setpar @r4 ,(r4)+ inc r0 ; packetlength := succ(packetlength) bic #^C77 ,r2 ; now drop the high bits from checks 20$: tochar r2 ,@r4 tst ranerr ; insert random checksum errors? beq 40$ ; no, please don't mov r0 ,-(sp) ;+ test mode call irand ;+ test mode tst r0 ;+ test mode bne 30$ ;+ test mode incb @r4 ;+ test mode 30$: mov (sp)+ ,r0 ;+ test mode 40$: setpar @r4 ,(r4)+ inc r0 ; packetlength := succ(packetlength) return global .sbttl try to handle half duplex handshake garbage ala IBM (barf) spakfi: save ; don't do this forever please call 200$ ; dump raw i/o first please unsave return 200$: bit #log$io ,trace ; dumping all i/o out ? beq 230$ ; no save ; save these please mov r1 ,r2 ; anything to do ? beq 220$ ; no 210$: clr r0 ; yes, dump ch by ch please bisb (r4)+ ,r0 ; get the next ch to dump mov #lun.lo ,r1 ; the lun to write to call putcr0 ; simple sob r2 ,210$ ; next please 220$: unsave ; pop and exit 230$: return ; bye global .enabl lsb spakwa: save tstb handch ; any paritcular handshake char today? beq 100$ ; no, just exit please scan @r5 ,#200$ tst r0 bne 100$ mov #200 ,r2 ; a limit on looping please 10$: calls binrea ,<#lun.ti,#4> ; wait for XON, max 4 seconds please tst r0 ; did the read timeout. if so, exit. bne 90$ ; exit and try to xon the link bicb #200 ,r1 ; insure no parity is set cmpb r1 ,handch ; is this the handshake character beq 100$ ; no, try again please sob r2 ,10$ ; not forever, please br 100$ ; bye 90$: save ; save error flags calls ttxon ,<#ttname,#lun.ti>; get the line turned on again please unsave ; pop error 100$: unsave ; pop loop index return .save .psect $PDATA ,D 200$: .byte msg$snd .byte msg$ser .byte msg$rcv .byte msg$command .byte msg$generic .byte 0 .even .restore .dsabl lsb global .sbttl rpack$ read incoming packet ; R P A C K $ ; ; rpack$(%loc data) ; ; input: @r5 buffer address ; 2(r5) data structure of 3 words to contain the ; returned length, number and type ; ; output: r0 error code if < 0, packet type if > 0 ; 255 for checksum error ; o$len = 0 ; offset for retruned packet length o$num = 2 ; offset for returned packet number o$type = 4 ; offset for returned packet type ; ; word 2 packet type ; word 1 packet number ; as in: 2(r5) ------> word 0 packet length ; ; ; ; local data offsets from r4 (allocated on the stack ; .done = 0 ; if <> 0 then we have the packet .type = 2 ; current type of packet .ccheck = 4 ; computed checksum .rcheck = 6 ; received checksum .len = 10 ; received pakcet length .timeo = 12 ; current timeout .num = 14 ; packet number, received .size = 16 ; current size of data portion .paksi = 20 ; for loop control for data portion .cbuff = 22 ; /42/ Mark checksum buffer address .hdtype = 24 ; /42/ .lsize = 26 ; total size of local data ; internal register usage: ; ; r0 error return ; r1 current character just read from remote ; r3 pointer to temp buffer containing the packet less the SOH ; and the checksum, used for computing checksum after the ; packet has been read. ; r4 pointer to local r/w data ; r5 pointer to argument list .sbttl rpack continued .iif ndf,$ALLSIZ, $ALLSIZ = >&177776 rpack$::save clr recbit ; /43/ Clear bit sum out sub #.lsize ,sp ; allocate space for local data mov sp ,r4 ; and point to it please sub #$ALLSIZ,sp ; /42/ Allocate huge buffer clr .num(r4) ; /41/ No fubar numbers on SOH tmo clr .size(r4) ; /41/ No fubar sizes on SOH timeout call waitsoh ; wait for a packet to start tst r0 ; did it work or did we timeout beq 5$ ; yes jmp 95$ ; we must have timed out then 5$: mov sp ,r3 ; the packet less SOH and checksum mov sp ,.cbuff(r4) ; /42/ Save start address clr .hdtype(r4) ; /42/ call rpakin ; initialize things 10$: tst .done(r4) ; while ( !done ) { bne 90$ ; ; call rpakrd ; Read the next character from bcs 95$ ; packet reader's buffer bisb r1 ,recbit ; /43/ So we can determine parity set bic #^C177 ,r1 ; Insure parity is cleared out cmpb r1 ,recsop ; If the character is senders SOH beq 80$ ; then we have to restart this else movb r1 ,(r3)+ ; *checkpacket++ = ch ; unchar r1 ,r0 ; Get the length packet next please mov r0 ,.hdtype(r4) ; /42/ Save header type cmp r0 ,#2 ; /42/ If the length is 0,1 or 2 then ble 15$ ; /42/ an extended header instead 14$: sub #2 ,r0 ; This is NOT an extended header so we sub chksiz ,r0 ; will check to see if the packet can bge 15$ ; hold at least SEQ+TYPE+CHECK clr r0 ; /44/ ;- add chksiz ,r0 ; Can't, thus we somehow lost the check ;- dec r0 ; sum type, so punt and reset it to a ;- movb #defchk ,chktyp ; type one checksum ;- mov #1 ,chksiz ; Fix the Checksum length also 15$: mov r0 ,.len(r4) ; Stuff the packet length call rpakrd ; As before, ask for the next character bcs 95$ ; and take an error exit if need be bisb r1 ,recbit ; /43/ So we can determine parity set bic #^C177 ,r1 ; Insure parity is cleared out cmpb r1 ,recsop ; If this is the sender's START_OF_PAK beq 80$ ; then it's time to restart the loop. movb r1 ,(r3)+ ; Insert the sequence number into the unchar r1 ,.num(r4) ; checksum packet and save the SEQ call rpakrd ; Read the TYPE field next, exiting bcs 95$ ; on a read error, of course. bisb r1 ,recbit ; /43/ So we can determine parity set bic #^C177 ,r1 ; Insure parity is cleared out cmpb r1 ,recsop ; As always, if we find the sender's beq 80$ ; START_OF_PACKET, the restart. movb r1 ,(r3)+ ; Save the TYPE field into the checksum mov r1 ,.type(r4) ; and also into the field for return. tst .hdtype(r4) ; /42/ NOW check for extended header. bne 19$ ; /42/ Not extended header. call rdexhd ; /42/ ReaD EXtended HeaDer tst r0 ; /42/ Did this work ok ? bgt 80$ ; /42/ No, got a RESYNCH bmi 96$ ; /42/ No, got a timeout or checksum 19$: mov .len(r4),.paksi(r4) ; loop for the data, if any mov @r5 ,r2 ; point to the buffer now 20$: tst .paksi(r4) ; for i := 1 to len do beq 30$ ; begin call rpakrd ; read(input,ch) bcs 95$ ; exit if error clrpar r1 ; ch := ch and chr(177B) cmpb r1 ,recsop ; if ch = SOH then resynch beq 80$ ; cmp .size(r4),#MAXLNG ; if currentsize < MAXPAKSIZE bhis 25$ ; then movb r1 ,(r2)+ ; data[i] := ch movb r1 ,(r3)+ ; checkpacket++ := ch ; end 25$: inc .size(r4) ; currentsize:=succ(currentsize) dec .paksi(r4) ; nchar_left := nchar_left - 1 br 20$ ; end 30$: clrb @r2 ; data[len] := NULL clrb @r3 ; checkpacket++ := null mov sp ,r3 ; reset base address of checkpacket call rpakck ; read the checksum now bcs 95$ ; exit on line error (like timeout) mov sp ,.done(r4) ; flag that we are done br 10$ ; check to see if we are done 80$: br 5$ ; synch error, restart the packet 90$: call rpakfi ; finish checksum and return the br 100$ 95$: mov 2(r5) ,r1 ; timeout error, flag no packet clr r0 ; nonfatal error for timout mov #timout ,o$type(r1) ; return as psuedo packet type mov #timout ,.type(r4) ; return as psuedo packet type 96$: call rpakst ; do stats and disk dumping now 100$: add #.lsize+$ALLSIZ,sp ; /42/ Pop local buffers unsave return global .sbttl Read extended header type 0 for long packets ; Added edit /42/ 08-Jan-86 16:32:59 Brian Nelson rdexhd: mov r5 ,-(sp) ; /42/ Need an ODD register for MUL mov r2 ,-(sp) ; /42/ Save R2 please call rpakrd ; /42/ Extended header, read the LENX1 bcs 90$ ; /42/ field, exiting on read errors. bic #^C177 ,r1 ; /42/ Insure parity is cleared out cmpb r1 ,recsop ; /42/ Exit if we find the SENDERS beq 80$ ; /42/ START_OF_HEADER please movb r1 ,(r3)+ ; /42/ Save into Checksum buffer unchar r1 ,r5 ; /42/ Get the high order of length mul #95. ,r5 ; /42/ Shift over please call rpakrd ; /42/ Extended header, read the LENX2 bcs 90$ ; /42/ field, exiting on read errors. bic #^C177 ,r1 ; /42/ Insure parity is cleared out cmpb r1 ,recsop ; /42/ Exit if we find the SENDERS beq 80$ ; /42/ START_OF_HEADER please movb r1 ,(r3)+ ; /42/ Save into Checksum buffer unchar r1 ,r1 ; /42/ Get the next one add r1 ,r5 ; /42/ Now we have the EXTENDED length sub chksiz ,r5 ; /42/ Drop it by checksum size mov r5 ,.len(r4) ; /42/ Save it here, of course mov .cbuff(r4),r5 ; /42/ Now, at LAST, get the extended mov #5 ,r1 ; /42/ header CHECKSUM data clr -(sp) ; /42/ Accum in stack 10$: clr r0 ; /42/ Use the normal SAFE way to add bisb (r5)+ ,r0 ; /42/ bytes even though we know for add r0 ,(sp) ; /42/ that no sign extends will happen sob r1 ,10$ ; /42/ Next please mov (sp)+ ,r0 ; /42/ Pop the checksum please mov r0 ,r2 ; /42/ Save it bic #^C300 ,r2 ; /42/ Compute it as in: ash #-6 ,r2 ; /42/ Chk=char((s+((s&0300)/0100))&77) add r0 ,r2 ; /42/ ... bic #^C77 ,r2 ; /42/ Got it now call rpakrd ; /42/ Extended header, read the HCHECK bcs 90$ ; /42/ field, exiting on read errors. bic #^C177 ,r1 ; /42/ Insure parity is cleared out cmpb r1 ,recsop ; /42/ Exit if we find the SENDERS beq 80$ ; /42/ START_OF_HEADER please movb r1 ,(r3)+ ; /42/ Save into Checksum buffer unchar r1 ,r1 ; /42/ Convert to actual checksum now cmpb r1 ,r2 ; /42/ Do the CHECKSUMS match ? bne 85$ ; /42/ No, exit with such set please clr r0 ; /42/ It worked, exit normally br 100$ ; /42/ bye... 80$: mov #1 ,r0 ; /42/ Resynch time br 100$ ; /42/ Exit 85$: mov #badchk ,r0 ; /42/ Header Checksum error br 95$ ; /42/ Stuff the error 90$: mov #timout ,r0 ; /42/ Return timeout error 95$: mov 2(sp) ,r5 ; /42/ Return timeout error mov 2(r5) ,r1 ; /42/ Get address of result block clr o$len(r1) ; /42/ Clear this also mov r0 ,o$type(r1) ; /42/ Return the error mov r0 ,.type(r4) ; /42/ Here also please mov #-1 ,r0 ; /42/ Fatal error 100$: mov (sp)+ ,r2 ; /42/ Pop r2 and mov (sp)+ ,r5 ; /42/ Restore R5 return .sbttl subroutines for RPACK only .enabl lsb rpakrd: calls binrea ,<#lun.ti,.timeo(r4)>; read(input,ch) tst r0 ; did it work bne 110$ ; no call rawio ; perhaps raw i/o logging bit #log$rp ,trace ; dump to a local terminal ? beq 20$ ; no cmpb r1 ,recsop ; start of a packet ? beq 10$ ; yes movb r1 ,-(sp) ; yes, stuff the ch onto the stack mov sp ,r1 ; point to it print r1 ,#1 ; dump it clr r1 ; restore what we read and exit bisb (sp)+ ,r1 ; restore it and exit br 20$ ; bye 10$: print #200$ ; start of a packet 20$: clr r0 ; no errors clc ; it worked return ; bye 110$: save ; save the error code calls ttxon ,<#ttname,#lun.ti>; get the line turned on again please unsave ; restore the error code sec ; flag the error return ; bye .save .psect $PDATA ,D 200$: .asciz // .even .restore .dsabl lsb rpakin: clr .done(r4) ; done := false clr .type(r4) ; packettype := 0 clr .ccheck(r4) ; checksum := 0 clr .rcheck(r4) ; received_checksum := 0 clr .len(r4) ; current length := 0 clr .num(r4) ; packet_number := 0 clr .timeo(r4) ; timeout := 0 clr .size(r4) ; current size of data part of packet clr .paksi(r4) ; loop control for data of packet mov @r5 ,r0 ; initialize the buffer to null mov #40 ,r1 10$: clrb (r0)+ ; simple clrb (r0)+ ; simple sob r1 ,10$ mov 2(r5) ,r0 ; return parameters clr (r0)+ ; packet.length := 0 clr (r0)+ ; packet.number := 0 clr (r0)+ ; packet.type := 0 call settmo mov r0 ,.timeo(r4) return settmo: mov sertim ,r0 ; if waiting for server command bne 20$ ; then use that timeout clr r0 ; bisb conpar+p.time,r0 ; get the remotes timeout bne 10$ ; ok mov #mytime ,r0 ; no good, setup a timeout 10$: cmpb r0,setrec+p.time ; use SET TIMEOUT value if > bhis 20$ ; no, use the timeout as in clr r0 ; ok, use the value the user said bisb setrec+p.time,r0 ; in the SET TIMEOUT command bne 20$ ; must be > 0 by now mov #mytime ,r0 ; no ?? 20$: return global .sbttl finish up rpack rpakfi: mov r3 ,-(sp) ; compute correct checksum type call checks ; simple mov (sp)+ ,.ccheck(r4) ; and stuff it in please cmpb .ccheck(r4),.rcheck(r4) ; compare computed checksum with the beq 100$ ; actual checksum mov #badchk ,.type(r4) ; flag checksum error 100$: mov 2(r5) ,r1 ; where to return some things mov .len(r4),o$len(r1) ; return the packet length mov .type(r4),o$type(r1) ; and the packet type mov .num(r4),o$num(r1) ; and at last, the packet number call rpakst ; do stats and logging now call rpaklo ; possibly log checksum errors? return .enabl lsb rpakst: cmpb .type(r4),#'A&137 ; count the packet types for stats blo 110$ ; bad packet type cmpb .type(r4),#'Z&137 ; must in the range A..Z bhi 110$ ; definiately a bad packet movb .type(r4),r1 ; packet is ok, add it to the stats sub #100 ,r1 ; convert to 1..26 asl r1 ; to word offsets asl r1 ; /43/ Double word offsets add #1 ,pcnt.r+2(r1) ; /43/ 32 bit addition today adc pcnt.r+0(r1) ; /43/ The high order part of it add #1 ,pcnt.r+2 ; /43/ Add it in here also adc pcnt.r+0 ; /43/ High order part 110$: bit #log$pa ,trace ; tracing today ? beq 120$ ; no calls dskdmp ,<#200$,.len(r4),.type(r4),.num(r4),@r5> 120$: return .save .psect $PDATA ,D 200$: .asciz /RPACK - / .even .restore .dsabl lsb .enabl lsb rpaklo: save cmp .rcheck(r4),.ccheck(r4) ; checksums match ? beq 100$ ; yes, do nothing then bit #log$io ,trace ; not if in raw i/o mode bne 100$ ; forget it sub #60 ,sp ; dump bad checksums out to disk mov sp ,r1 ; point to the buffer copyz #200$ ,r1 ; a header strlen r1 ; length so far add r0 ,r1 ; point to the end of it deccvt .rcheck(r4),r1 ; convert to decimal add #6 ,r1 ; move along please deccvt .ccheck(r4),r1 ; the calculated checksum add #6 ,r1 ; make it .asciz clrb @r1 ; simple mov sp ,r1 ; point back to the buffer strlen r1 ; get the length calls putrec ,; dump buffer to disk add #60 ,sp ; pop buffer and exit 100$: unsave ; pop r0 and exit return .save .psect $PDATA ,D 200$: .asciz /?Bad Checksum: rcv,calc are / .even .restore .dsabl lsb global .sbttl read and convert the checksum for RPACK rpakck: save ; use r3 for accumulating check clr r3 ; assume zero for now call rpakrd ; read(input,ch) bcs 110$ ; exit if error bisb r1 ,recbit ; recbit |= ch ; bic #^c177 ,r1 ; ch := ch and 177B unchar r1 ,r3 ; received_check := ch cmpb chktyp ,#defchk ; if len(checksum) > 8bits blos 10$ ; then begin ash #6 ,r3 ; check := check * 64 call rpakrd ; read(input,ch) bcs 110$ ; exit if error bic #^c177 ,r1 ; ch := ch and 177B unchar r1 ,r1 ; ch := unchar(ch) bisb r1 ,r3 ; rcheck := rcheck + ch cmpb chktyp ,#'3 ; if checktype = CRC16 bne 10$ ; then ash #6 ,r3 ; begin call rpakrd ; check := check * 64 bcs 110$ ; check := check + ch bic #^c177 ,r1 ; ch := ch and 177B unchar r1 ,r1 ; bisb r1 ,r3 ; end ; 10$: clc br 120$ 110$: sec 120$: mov r3 ,.rcheck(r4) ; return the checksum unsave return .sbttl parity routines ; C L R P A R ; ; input: 2(sp) the character to clear parity for ; output: 2(sp) the result ; ; caller by CLRPAR macro ; ; If parity is set to anything but NONE then always ; clear the parity out else clear it if and only if ; filetype is not image mode. clrpar::tstb parity ; handle nothing please (no parity) beq 10$ ; yes cmpb parity ,#par$no ; set parity none used ? bne 20$ ; no, must be some other type 10$: tst image ; no parity, image mode today ? bne 100$ ; yes, leave things alone please 20$: bic #^C177 ,2(sp) ; no, clear bits 7-15 please 100$: return ; bye global .sbttl compute proper checksum please ; C H E C K S ; ; input: 2(sp) address of .asciz string to compute checksum for ; output: @sp the computed checksum checks::save ; save registers we may use mov 12(sp) ,r2 ; point to the string to do it for clr 12(sp) ; assume a zero checksum ? cmpb chktyp ,#'3 ; CRC-CCITT type today ? bne 5$ ; no strlen r2 ; yes, get the .asciz string length calls crcclc , ; compute the CRC16-CCITT mov r0 ,r2 ; stuff the result into r2 for later br 90$ ; and exit 5$: clr r1 ; init the checksum accumulator 10$: clr r3 ; get the next ch please bisb (r2)+ ,r3 ; got the next ch now beq 20$ ; hit the end of the string cmpb parity ,#par$no ; did the packet contain parity? beq 15$ ; no, leave bit 7 alone bic #^C177 ,r3 ; yes, please clear bit seven 15$: bic #170000 ,r1 ; /42/ Insure long packet not overflow add r3 ,r1 ; check := check + ch br 10$ 20$: mov r1 ,r2 ; checksum := (((checksum and 300B)/64) cmpb chktyp ,#'2 ; 12 bit sum type checksum ? beq 30$ ; yes, just exit bic #^C300 ,r2 ; +checksum) and 77B) ash #-6 ,r2 ; add r1 ,r2 ; bic #^C77 ,r2 br 90$ 30$: bic #170000 ,r2 ; type 2 checksum 90$: mov r2 ,12(sp) ; return the checksum 100$: unsave ; exit return .sbttl crc calculation ; This routine will calculate the CRC for a string, using the ; CRC-CCIT polynomial. ; ; The string should be the fields of the packet between but ; not including the and the block check, which is ; treated as a string of bits with the low order bit of the ; first character first and the high order bit of the last ; character last -- this is how the bits arrive on the ; transmission line. The bit string is divided by the ; polynomial ; ; x^16+x^12+x^5+1 ; ; The initial value of the CRC is 0. The result is the ; remainder of this division, used as-is (i.e. not ; complemented). ; ; From 20KERMIT.MAC, rewritten for PDP11 by Brian Nelson ; 13-Jan-84 08:50:43 ; ; input: @r5 string address ; 2(r5) string length ; output: r0 crc crcclc::save ; save registers please clr r0 ; initialize the CRC to zero mov @r5 ,r3 ; get the string address now mov 2(r5) ,r4 ; get the string length beq 100$ ; oops, nothing to do then 10$: clr r1 ; get the next character please bisb (r3)+ ,r1 ; please avoid pdp11 sign extend cmpb parity ,#par$no ; did the packet have parity? beq 20$ ; no, leave bit seven alone bic #^C177 ,r1 ; yes, clear bit seven please 20$: ixor r0 ,r1 ; add in with the current CRC mov r1 ,r2 ; get the high four bits ash #-4 ,r2 ; and move them over to 3..0 bic #^C17 ,r2 ; drop any bits left over bic #^C17 ,r1 ; and the low four bits asl r1 ; times 2 for word addressing asl r2 ; times 2 for word addressing mov crctb2(r1),r1 ; get low portion of CRC factor ixor crctab(r2),r1 ; simple (limited modes for XOR) swab r0 ; shift off a byte from previous crc bic #^C377 ,r0 ; clear new high byte ixor r1 ,r0 ; add in the new value sob r4 ,10$ ; next please 100$: unsave ; pop saved r1-r5 return ; Data tables for CRC-CCITT generation .save .psect $PDATA ,D crctab: .word 0 .word 10201 .word 20402 .word 30603 .word 41004 .word 51205 .word 61406 .word 71607 .word 102010 .word 112211 .word 122412 .word 132613 .word 143014 .word 153215 .word 163416 .word 173617 crctb2: .word 0 .word 10611 .word 21422 .word 31233 .word 43044 .word 53655 .word 62466 .word 72277 .word 106110 .word 116701 .word 127532 .word 137323 .word 145154 .word 155745 .word 164576 .word 174367 .restore .sbttl clear stats out ; C L R S T A ; ; clear out the packet counts by packet type from the last ; transaction and add them into the total running count by ; packet type. clrsta::save ; save the registers we use mov #pcnt.r ,r1 ; packets received mov totp.r ,r2 ; running count so far mov #34 ,r0 ; number of works to add/clear 10$: add 2(r1) ,2(r2) ; /43/ Add in the totals adc (r2) ; /43/ The carryover also add (r1) ,(r2)+ ; /43/ The HIGH order of it tst (r2)+ ; /43/ Get to the next one clr (r1)+ ; /43/ Clear of old stuff out clr (r1)+ ; /43/ Clear of old stuff out sob r0 ,10$ ; /43/ Next please mov #pcnt.s ,r1 ; now for the packets sent mov totp.s ,r2 ; where to add them in mov #34 ,r0 ; number of words to do 20$: add 2(r1) ,2(r2) ; /43/ Add in the totals adc (r2) ; /43/ The carryover also add (r1) ,(r2)+ ; /43/ The HIGH order of it tst (r2)+ ; /43/ Get to the next one clr (r1)+ ; /43/ Clear of old stuff out clr (r1)+ ; /43/ Clear of old stuff out sob r0 ,20$ ; /43/ Next please clr pcnt.n ; naks count clr pcnt.n+2 ; /43/ rest of it clr pcnt.t ; /44/ Timeouts clr pcnt.t+2 ; /44/ Timeouts clr filein+0 ; /43/ File data stats clr filein+2 ; /43/ File data stats clr fileout+0 ; /43/ File data stats clr fileout+2 ; /43/ File data stats clr charin+0 ; /43/ Physical link stats clr charin+2 ; /43/ Physical link stats clr charout+0 ; /43/ Physical link stats clr charout+2 ; /43/ Physical link stats unsave ; pop the registers we used return ; and exit incsta::call seconds ; /43/ Get current seconds since mov #times+4,r2 ; /43/ midnight, moving old times mov r0 ,(r2)+ ; /43/ Insert NEW times first mov r1 ,(r2) ; /43/ then subtact off the old sub times+2 ,(r2) ; /43/ times from it sbc -(r2) ; /43/ ditto for the carry sub times ,(r2) ; /43/ Incremental is in times+4 mov r1 ,-(r2) ; /43/ and times+6, new time is in mov r0 ,-(r2) ; /43/ times+0 and time+2 return ; /43/ Exit global global ; /43/ global ; /44/ .sbttl waitsoh wait for a packet start (ascii 1, SOH) ; W A I T S O H ; ; input: nothing ; output: r0 error code ; r1 the SOH or NULL if we timed out ; ; ; As of edit 2.41 (25-Dec-85 13:26:26) from Steve Heflin we will ; exit Kermit-11 if we find that the first thing we find is a CTL ; Z (\032). This is desired in case the user accidentilly put the ; Kermit-11 into server without setting a line. ; On edit /44/, wait for TWO control z's in a row to exit. waitsoh:clr r1 ; Start with nothing clr -(sp) ; /56/ Hold virgin copy of data mov #2 ,-(sp) ; /44/ Counter for control Z's 10$: cmpb r1 ,recsop ; wait for a packet header please beq 40$ ; ok, exit call settmo ; get proper timeout set up calls binrea ,<#lun.ti,r0> ; read with timeout mov r1 ,2(sp) ; /56/ Save it bic #^C177 ,r1 ; /44/ Never want parity here tst r0 ; did the read work ? bne 30$ ; oops, just exit then cmpb r1 ,#'Z&37 ; /41/ Control Z returned ? bne 15$ ; /41/ No dec (sp) ; /44/ Should we REALLY exit now? bne 20$ ; /44/ No, in case we got some NOISE call clostt ; /41/ Yes, drop terminal and exit jmp exit ; /41/ Bye now 15$: mov #2 ,(sp) ; /44/ Need TWO ^Z's in a row to exit 20$: call rawio ; all is not well, perhaps dump packets br 10$ ; loop back for finding a PACKET start 30$: clr r1 ; Timeout, return( NULL ) br 100$ ; /56/ 40$: bitb #200 ,2(sp) ; /56/ Parity perhaps? beq 100$ ; /56/ No cmpb parity ,#PAR$NONE ; /56/ 8bit channel? bne 100$ ; /56/ No inc incpar ; /56/ Yes, also want message only once 100$: cmp (sp)+ ,(sp)+ ; /56/ Pop control Z counter return ; exit global GLOBAL rawio: bit #log$io ,trace ; dumping all i/o today? beq 100$ ; no save ; yes, save these please clr r0 bisb r1 ,r0 ; and setup call to putcr0 mov #lun.lo ,r1 ; the unit to write to call putcr0 ; simple unsave ; pop these now 100$: return .sbttl initialize repeat count for sending inirepeat:: save clr dorpt ; assume not doing repeat things tst setrpt ; user disable repeat count processing? beq 100$ ; yes cmpb #myrept ,#40 ; am I doing it ? beq 100$ ; no, just exit then clr rptcount ; size of repeat if zero clr rptlast ; no last character please (a null) mov #-1 ,rptinit ; need to prime the pump please movb conpar+p.rept,r0 ; check for doing so beq 100$ ; no cmpb r0 ,#40 ; a space also ? beq 100$ ; yes cmpb r0 ,senpar+p.rept ; same ? bne 100$ ; no movb r0 ,rptquo ; yes, save it mov #-1 ,dorpt ; and we are indeed doing this 100$: clc unsave return global .sbttl BUFFIL buffer from the file that is being sent ; B U F F I L ; ; input: @r5 buffer address ; output: r0 rms sts error code ; r1 length of the string buffil::save ; save all registers we may use mov @r5 ,r4 ; point to the destination address clr r3 ; use as a length counter clr r5 ; bitb #CAPA.L,conpar+p.capas ; /42/ Check to see if both sides beq 4$ ; /42/ REALLY understand long packets bitb #CAPA.L,senpar+p.capas ; /42/ We would normally but it is beq 4$ ; /42/ possible to SET NOLONG mov senlng ,r5 ; /42/ Does receiver understand bne 5$ ; /42/ long packets today? 4$: bisb conpar+p.spsiz,r5 ; get the recievers maximum size 5$: sub #14 ,r5 ; being overcautious today ? 10$: tst dorpt ; are we doing repeat counts beq 50$ ; no 15$: call gnc ; getnext character ; bcs 30$ ; if ( error ) then break ; tst rptinit ; if ( firsttime ) beq 20$ ; then clr rptinit ; rptinit = 0 ; clr rptcount ; rptcount = 0 ; movb r1 ,rptlast ; rptlast = ch ; 20$: cmpb r1 ,rptlast ; if ( ch == rptlast ) bne 30$ ; then cmp rptcount,#94. ; bge 30$ inc rptcount ; rptcount++ ; br 15$ ; else break ; 30$: mov r1 ,rptsave ; save the failed character please tst rptcount ; this may be EOF on first character beq 90$ ; if so, we simply do nothing at all cmp rptcount,#2 ; please don't bother with ONE char. bgt 40$ ; don't waste the overhead for two 35$: clr r1 ; avoid sign extension please bisb rptlast ,r1 ; get the character to write call 200$ ; and stuff it into the buffer dec rptcount ; more to insert ? bne 35$ ; yes br 45$ ; no, exit 40$: movb rptquo ,(r4)+ ; insert the repeat count quote inc r3 ; count it in the packet size tochar rptcount,(r4)+ ; convert the repeat count to a char inc r3 ; and count in the packet size clr r1 ; bisb rptlast ,r1 ; and insert the repeated character call 200$ ; insert it into the buffer 45$: movb rptsave ,rptlast ; make the failing character the one clr rptcount ; in case of EOF, set this please tst r0 ; was this the end of file ? bne 90$ ; yes, we had better leave then inc rptcount ; no, initialize the count please br 70$ ; and check for overflow in the buffer 50$: call gnc ; getnextchar ; bcs 90$ ; if ( eof ) then break ; call 200$ ; get the character stuff w/o repeats 70$: cmp r3 ,r5 ; room for the data ? blo 10$ ; end 90$: mov r3 ,r1 ; return the length please beq 100$ ; nothing there clr r0 ; say read was successful 100$: unsave ; and exit return .sbttl actually quote and stuff the character in for BUFFIL 200$: tst do8bit ; exit if status <> success; beq 210$ ; if need_8_bit_prefix tstb r1 ; and bit_test(ch,200B) bpl 210$ ; then begin movb ebquot ,(r4)+ ; buffer[i] := eight_bit_quote inc r3 ; i := succ(i) bicb #200 ,r1 ; ch := bit_clear(ch,200b) 210$: clr r2 ; end ; bisb r1 ,r2 ; ch0_7 := ch bic #^C177 ,r2 ; ch0_7 := ch0_7 and 177B cmpb r2 ,#SPACE ; if ch0_7 < space blo 220$ ; or cmpb r2 ,#DEL ; ch0_7 = del beq 220$ ; or cmpb r2 ,senpar+p.qctl ; ch0_7 = quote beq 220$ ; or tst do8bit ; ( need_8_bit_prefix ) beq 215$ ; and ( ch0_7 == binaryquote ) cmpb r2 ,ebquot ; beq 220$ ; or 215$: tst dorpt ; ( doing_repeatcompression ) beq 230$ ; and ( ch0_7 == repeatquote ) cmpb r2 ,rptquo ; bne 230$ ; then ; begin 220$: movb senpar+p.qctl,(r4)+ ; buffer[i] := quote inc r3 ; length := succ(length) cmpb r2 ,#37 ; if ( ch0_7 < SPACE ) blos 225$ ; or cmpb r2 ,#del ; ( ch0_7 == DEL ) bne 230$ ; then 225$: ctl r1 ,r1 ; ch := ctl(ch) ctl r2 ,r2 ; ch0_7 := ctl(ch0_7) 230$: tst image ; if image_mode beq 240$ ; then movb r1 ,(r4)+ ; buffer[i] := ch br 250$ ; else 240$: movb r2 ,(r4)+ ; buffer[i] := ch0_7 250$: inc r3 ; length := succ( length ) return gnc: mov #lun.in ,r0 add #1 ,fileout+2 ; /43/ Stats on file data adc fileout+0 ; /43/ 32 bits call getcr0 tst r0 beq 100$ sec return 100$: clc return global .sbttl bufpak buffil but get data from a buffer ; input: @r5 source buffer, .asciz ; output: 2(r5) destination buffer ; r0 zero (ie, no errors are possible) ; r1 string length ; ; No 8 bit prefixing and no repeat counts will be done. ; This routine is used for encoding string to be sent as ; generic commands to a server. bufpak::save ; save all registers we may use mov 2(r5) ,r4 ; point to the destination address mov @r5 ,r5 ; the source string clr r3 ; use as a length counter 10$: clr r1 ; ch := buffer[i] bisb (r5)+ ,r1 ; avoid PDP-11 sign extension beq 90$ ; clr r2 ; bisb r1 ,r2 ; ch0_7 := ch ' bic #^C177 ,r2 ; ch0_7 := ch0_7 and 177B cmpb r2 ,#space ; if ch0_7 < space blo 20$ ; or cmpb r2 ,#del ; ch0_7 = del beq 20$ ; or cmpb r2 ,senpar+p.qctl ; ch0_7 = quote bne 40$ ; then ; begin 20$: movb senpar+p.qctl,(r4)+ ; buffer[i] := quote inc r3 ; length := succ(length) cmpb r2 ,senpar+p.qctl ; if ch0_7 <> quote beq 30$ ; then begin ctl r1 ,r1 ; ch := ctl(ch) ctl r2 ,r2 ; ch0_7 := ctl(ch0_7) end 30$: ; end 40$: tst image ; if image_mode beq 50$ ; then movb r1 ,(r4)+ ; buffer[i] := ch br 60$ ; else 50$: movb r2 ,(r4)+ ; buffer[i] := ch0_7 60$: inc r3 ; length := succ( length ) 70$: clr -(sp) bisb conpar+p.spsiz,@sp ; exit if length > spsize-8 bne 80$ ; if spsiz = 0 mov #maxpak ,@sp ; then maxsize := #maxpak 80$: sub #10 ,@sp ; cmp r3 ,(sp)+ ; blo 10$ ; end 90$: mov r3 ,r1 ; return the length please clr r0 ; say read was successful unsave ; and exit return .sbttl bufemp dump a buffer out to disk ; B U F E M P ; ; bufemp(%loc buffer,%val len) ; ; input: @r5 buffer address ; 2(r5) length ; output: r0 error bufemp::save ; save temps as usual mov @r5 ,r2 ; input record address mov 2(r5) ,r3 ; string length clr r0 ; insure no error for a null packet 10$: tst r3 ; anything left in the record? ble 100$ ; no 20$: clr r0 ; get the next character bisb (r2)+ ,r0 ; into a convienient place dec r3 ; chcount-- ; mov #1 ,r4 ; repeat_count = 1 ; tst dorpt ; are we doing repeat count stuff? beq 30$ ; no cmpb r0 ,rptquo ; yes, is this the aggreed upon prefix? bne 30$ ; no dec r3 ; chcount-- clr r4 ; yes, get the next character then bisb (r2)+ ,r4 ; and decode it into a number bic #^C177 ,r4 ; insure no parity bits are hanging unchar r4 ,r4 ; simple to do clr r0 ; now prime CH with the next character bisb (r2)+ ,r0 ; so we can check for other types of dec r3 ; quoting to be done. tst r4 ; insure the count is legitimate bgt 30$ ; it's ok mov #1 ,r4 ; it's fubar, fix it 30$: clr set8bit ; assume we don't have to set bit 7 tst do8bit ; must we do 8 bit unprefixing? beq 60$ ; no cmpb r0 ,ebquot ; yes, is this the 8 bit prefix? bne 60$ ; no mov sp ,set8bit ; yes, send a flag to set the bit clr r0 ; and get the next character bisb (r2)+ ,r0 ; without sign extension dec r3 ; one less character left in buffer 60$: cmpb r0 ,conpar+p.qctl ; is this a quoted character? bne 70$ ; no clr r0 ; yes, get the next character bisb (r2)+ ,r0 ; must be one you know dec r3 ; chcount := pred(chcount) clr r1 ; must avoid sign extension here bisb r0 ,r1 ; check low 7 bits against quote bic #^C177 ,r1 ; drop 7..15 cmpb r1 ,conpar+p.qctl ; if ch <> myquote beq 70$ ; then cmpb r1 ,#77 ; if ( ch & 177 ) >= ctl(DEL) blo 70$ ; and ( ch & 177 ) <= ctl(del)+40 cmpb r1 ,#137 ; then bhi 70$ ; ch = ctl(ch) ; ctl r0 ,r0 ; 70$: tst set8bit ; do we need to set the high bit? beq 74$ ; no bisb #200 ,r0 ; yes, set the bit on please 74$: mov r0 ,-(sp) ; and save the character to write 75$: mov #lun.ou ,r1 ; channel_number := lun.out tst outopn ; is there really something open? bne 80$ ; yes, put the data to it clr r1 ; no, direct the output to a terminal 80$: mov @sp ,r0 ; restore the character to write out call putcr0 ; and do it add #1 ,filein+2 ; /43/ Stats adc filein+0 ; /43/ 32 bits worth sob r4 ,75$ ; duplicate the character if need be. tst (sp)+ ; pop the stack where we saved CH br 10$ ; next character please 100$: unsave return global global .sbttl bufunpack like bufemp, but return data to a buffer ; input: @r5 source buffer, .asciz ; output: 2(r5) destination buffer ; r0 zero (ie, no errors are possible) ; r1 string length ; ; No 8 bit prefixing and no repeat counts will be done. ; This routine is used for decoding strings received for ; generic commands to the server. bufunp::save ; save temps as usual mov @r5 ,r2 ; input record address clr r3 ; length := 0 mov 2(r5) ,r4 ; resultant string ; 10$: clr r0 ; get the next character bisb (r2)+ ,r0 ; into a convienient place beq 100$ ; All done bic #^C177 ,r0 ; /53/ Always seven bit data mov #1 ,r5 ; /53/ Assume character not repeated tst dorpt ; /53/ Repeat processing off? beq 20$ ; /53/ Yes, ignore. cmpb r0 ,rptquo ; /53/ Is this a repeated char? bne 20$ ; /53/ No, normal processing bisb (r2)+ ,r5 ; /53/ Yes, get the repeat count bic #^C177 ,r5 ; /53/ Always seven bit data unchar r5 ,r5 ; /53/ Get the value tst r5 ; /53/ Good data bgt 15$ ; /53/ Yes mov #1 ,r5 ; /53/ No, fix it 15$: clr r0 ; /53/ Avoid sign extension bisb (r2)+ ,r0 ; /53/ Now get the real data bic #^C177 ,r0 ; /53/ Always seven bit data 20$: cmpb r0 ,senpar+p.qctl ; is this a quoted character? bne 30$ ; no clr r0 ; yes, get the next character bisb (r2)+ ,r0 ; must be one you know clr r1 ; must avoid sign extension here bisb r0 ,r1 ; check low 7 bits against quote bic #^C177 ,r1 ; drop 7..15 cmpb r1 ,senpar+p.qctl ; if ch <> myquote beq 30$ ; then ctl r0 ,r0 ; ch := ctl(ch); 30$: movb r0 ,(r4)+ ; copy the byte over now inc r3 ; length := succ(length) sob r5 ,30$ ; /53/ Perhaps data was repeated br 10$ ; next character please 100$: clrb @r4 ; make the string .asciz mov r3 ,r1 ; return the length clr r0 ; fake no errors please unsave ; pop registers and exit return global .sbttl printm print message if not remote ; P R I N T M ; ; input: @r5 arg count ; 2(r5) text for message #1 ; 4(r5) and so on .enabl lsb printm::save ; save registers we will use mov (r5)+ ,r1 ; get the message count beq 100$ ; nothing to do tst inserv ; skip if a server bne 100$ ; bye tst remote ; skip if we are the remote bne 100$ ; yep message message ; a header 10$: mov (r5)+ ,r0 .print r0 ; now loop thru printing the stuff sob r1 ,10$ ; next please message ; a clr logini ; may need a logging header 100$: unsave ; pop temps return ; and exit global .dsabl lsb .sbttl error message printing ; E R R O R ; ; error(%val msgcount,%loc msg1, %loc msg2,....) ; ; Error sends the message text if we are remote else ; it prints it out as in the baseline KERMIT.C erbfsiz = 84. error:: save tst remote ; if not remote then printm(...) bne 10$ ; we are the remote. send errors call printm ; simple br 100$ ; bye 10$: mov (r5)+ ,r1 ; message count beq 100$ ; nothing to do ? sub #erbfsiz+2,sp ; remote, allocate a text buffer mov sp ,r4 ; and point to it please movb #'% ,(r4)+ ; /35/ insert dec style 'warning' mov #erbfsiz-1,r2 ; length so far mov #prompt ,r0 ; /32/ insert prompt into error text 20$: movb (r0)+ ,(r4)+ ; /32/ copy the prompt text over beq 25$ ; /32/ all done, found a null (asciz) dec r2 ; /32/ one less place to store text br 20$ ; /32/ next prompt character please 25$: dec r4 ; /32/ backup to the null we copied. cmpb -1(r4) ,#'> ; /35/ get rid of the trailing '>' bne 26$ ; /35/ no movb #'- ,-1(r4) ; /35/ change it to form 'Kermit-11-' 26$: movb #40 ,(r4)+ ; /32/ insert a space into buffer dec r2 ; /32/ one less available tst r2 ; /32/ did we possibly run out of room? bgt 30$ ; /32/ no mov sp ,r4 ; /32/ yes, forget about the prompt. mov #erbfsiz,r2 ; /32/ yes, also reset the space avail 30$: mov (r5)+ ,r3 ; get the next message please 40$: movb (r3)+ ,@r4 ; now copy it to the buffer until beq 50$ ; we get an ascii null (chr(0)) cmpb @r4 ,#'$ ; apparently CPM systems don't like bne 45$ ; dollar symbols ? movb #'_ ,@r4 ; so stuff a '_' in instead 45$: inc r4 sob r2 ,40$ ; no, go until we get one or run br 60$ ; out of space to put it 50$: movb #40 ,(r4)+ ; insert a space in there dec r2 ; insure sufficient space beq 60$ ; no sob r1 ,30$ ; and get the next message 60$: clrb @r4 ; inaure .asciz mov sp ,r4 ; all done, send the ERROR packet strlen r4 ; get the length spack #'E,paknum,r0,r4 ; and send it add #erbfsiz+2,sp ; deallocate the text buffer 100$: unsave ; and exit return global .sbttl print received error packet out ; P R E R R P ; ; prerrp(%loc msg) ; ; input: @r5 address of .asciz string to print .enabl lsb prerrp::.print #200$ .print @r5 .newli clr logini return .save .psect $PDATA ,D .enabl lc 200$: .asciz /Aborting with error from remote./ .even .restore .dsabl lsb global .sbttl send/print several common types of errors ; M$TYPE(%val(type),%loc(packet)) unknown packet type recieved ; M$RETRY retry abort ; M$SYNCH out of synch ; ; 18-Oct-84 17:34:37 BDN debugging for PRO/RT11 Kermit m$type::save ; save temps that we will use clr -(sp) ; a buffer for the packet type movb @r5 ,@sp ; the packet type mov sp ,r0 ; point back to the buffer calls error ,<#4,#e$type,r0,#e$hd,2(r5)> tst (sp)+ ; pop local buffer unsave ; pop temp and exit return m$retr::save ; save r0 please bitb #200 ,recbit ; /44/ Perhaps parity was going ? beq 10$ ; /44/ No cmpb parity ,#PAR$NO ; /44/ Yes, do we know about parity bne 10$ ; /44/ Yes we do, normal abort calls error ,<#1,#e$par> ; /44/ No parity, ctl fields have br 100$ ; /44/ Exit 10$: calls error ,<#1,#e$retr> ; send/print the error message 100$: unsave ; pop and exit return ; bye m$sync::save ; save r0 please calls error ,<#1,#e$synch> ; send/print the error message unsave ; pop and exit return ; bye .save .psect $pdata e$hd: .asciz / pak: / e$type: .asciz /Fubar pak type: / e$retr: .asciz /Retry limit reached/ e$synch:.asciz /Hopelessly out of synch with sending Kermit/ e$par: .asciz /Retry limit reached, parity is possibly being introduced/ .even .restore .sbttl get next file to send ; G E T N X T ; ; input: srcnam possibly wildcarded filename ; index flag if eq 0 then this is the first time thru ; output: filnam next file to do ; r0 <> 0 then abort ; ; RSTS and RSX11M/M+ ; ; Lookup uses the RMS version 2 $SEARCH macro to do the directory ; operation. For RT11 we will simply NOP the $SEARCH since RT11 ; does not support directory lookup operations in the EXEC. Thus ; the error codes ER$NMF (no more files) and ER$FNF are referenced ; directly here. getnxt::save calls lookup ,<#3,#srcnam,#index,#filnam> tst r0 ; did it work ? beq 100$ ; yes cmp r0 ,#ER$NMF ; no more files matching name ? beq 20$ ; yes, we are all done then cmp r0 ,#ER$FNF ; how about file not found ? bne 30$ ; no, print the error message out 20$: tst index ; sent any files yet ? bne 100$ ; yes, that's ok then mov #ER$FNF ,r0 ; no, convert ER$NMF to ER$FNF 30$: mov r0 ,-(sp) ; save r0 please calls syserr , ; not so good. Get the error text mov #filnam ,r1 ; assume the filename parse worked calls fparse ,<#srcnam,#filnam>; quite possibly it may not have tst r0 ; so decide whether to send the beq 40$ ; origonal name or the expanded mov #srcnam ,r1 ; filename in the error packet. 40$: calls error ,<#2,#errtxt,r1>; and send/print it out mov (sp)+ ,r0 ; pop saved error code from lookup 100$: unsave return global .sbttl xor and scanch l$xor:: save mov 4(sp) ,r0 ixor #100 ,r0 mov r0 ,4(sp) unsave return ; S C A N C H ; ; input: 4(sp) the string address ; 2(sp) the character to look for ; output: r0 position of ch in string scanch::save ; save temps mov 6(sp) ,r2 ; get address of the string clr r0 ; initial found position 10$: tstb @r2 ; end of the string yet ? beq 90$ ; yes inc r0 ; no, pos := succ(pos) cmpb 4(sp) ,(r2)+ ; does the ch match the next one? bne 10$ ; no, try again br 100$ ; yes, exit loop 90$: clr r0 ; failure, return postion = 0 100$: unsave ; pop r2 mov @sp ,4(sp) ; move return address up cmp (sp)+ ,(sp)+ ; pop stack return ; and exit ; random things for testing irand:: tst testc bne 10$ mov #1234. ,testc 10$: mov testc ,r0 mov r1 ,-(sp) mov r0 ,r1 ash #-4 ,r1 bic #170000 ,r1 xor r1 ,r0 ash #13 ,r1 bic #100000 ,r1 xor r1 ,r0 bic #100000 ,r0 mov r0 ,testc ash #-13 ,r0 mov (sp)+ ,r1 return global .sbttl compute parity for an outgoing 8 bit link ; This is software parity generation as some DEC interfaces ; and some DEC executives don't know how to compute parity. ; There are two methods given here for ODD and EVEN genera- ; tion. One is from Frank da Cruz's 20KERMIT.MAC and does it ; by computing it. The other method is from the pascal RT11 ; Kermit (by Phil Murton) and does a table lookup to compute ; the parity. For the sake of speed and the fact that some RT ; systems lack certain instructions we will use the later ; method at a slight cost in space. parlok = 1 ; use table lookup method .assume par$od eq 1 ; set parity odd .assume par$ev eq 2 ; set parity even .assume par$ma eq 3 ; set parity mark .assume par$sp eq 4 ; set parity space .assume par$no eq 5 ; set parity none .psect $pdata pardsp: .word none.p, odd.p, even.p ,mark.p ,spac.p ,none.p .psect $code dopari::save ; save things we will use mov parity ,r3 ; get the current parity setting asl r3 ; times 2 mov 12(sp) ,r1 ; get the character to do it to jsr pc ,@pardsp(r3) ; and dispatch as desired mov r1 ,12(sp) ; return the character please unsave ; pop and exit return none.p: return ; do nothing mark.p: bisb #200 ,r1 ; mark means we are always high return ; on bit seven spac.p: bicb #200 ,r1 ; space means we are always low return ; on bit seven .sbttl odd/even parity generation .if eq ,parlok ; what kind of parity generation .ift ; to use even.p: bic #^c177 ,r1 ; insure no high bits are set mov r1 ,r2 ; copy call par ; and do it return odd.p: bic #^c177 ,r1 ; insure only bits 0..6 mov r1 ,r2 ; copy it bisb #200 ,r2 ; and set bit seven call par ; do it return ; bye par: mov #200 ,r3 ; xor instruction is strange ash #-4 ,r2 ; move the high four bits down bic #^C17 ,r2 ; clear bit 7's right propagation ixor r1 ,r2 ; fold source character into one bic #^C17 ,r2 ; insure we have only 4 bits today mov r2 ,r3 ; now check if bits 2 and 3 are asr r3 ; /2 asr r3 ; /2 cmpb r3 ,#3 ; both high or both low beq 10$ ; both high tstb r3 ; both low ? bne 20$ ; no, don't set any parity then 10$: ixor #200 ,r1 ; yes, toggle parity now 20$: bic #^C3 ,r2 ; ok, now see if the low 2 bits are cmpb r2 ,#3 ; both either on or off beq 30$ ; both are on, set parity tstb r2 ; perhaps only one bit is on? bne 40$ ; yep 30$: ixor #200 ,r1 ; toggle the bit then 40$: return ; bye .endc ; if eq, parlok .sbttl odd/even parity generation via lookup .if ne ,parlok ; use this method ? .ift ; yes odd.p: bic #^c177 ,r1 tstb partab(r1) bne 100$ bisb #200 ,r1 100$: return even.p: bic #^c177 ,r1 tstb partab(r1) beq 100$ bisb #200 ,r1 100$: return ; Table of parity setting for ascii 0-177 ; From Phil Murton's RTLINE.PAS .save .psect $PDATA ,D partab: .byte 0,1,1,0,1,0,0,1 ; first 8 ascii characters .byte 1,0,0,1,0,1,1,0 .byte 1,0,0,1,0,1,1,0 .byte 0,1,1,0,1,0,0,1 .byte 1,0,0,1,0,1,1,0 .byte 0,1,1,0,1,0,0,1 .byte 0,1,1,0,1,0,0,1 .byte 1,0,0,1,0,1,1,0 .byte 1,0,0,1,0,1,1,0 .byte 0,1,1,0,1,0,0,1 .byte 0,1,1,0,1,0,0,1 .byte 1,0,0,1,0,1,1,0 .byte 0,1,1,0,1,0,0,1 .byte 1,0,0,1,0,1,1,0 .byte 1,0,0,1,0,1,1,0 .byte 0,1,1,0,1,0,0,1 ; last eight ascii characters (to 177) .restore .endc ; if ne, parlok .end