.title k11pak packet driver for kermit-11 .ident /8.0.01/ .dsabl gbl ; Brian Nelson 30-Nov-83 10:20:09 ; ; 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 ; K11DEF.MAC defines all the packet types .include /IN:K11MAC.MAC/ .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 ; i can receive attributes 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.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 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 spack$::save ; save registers that we may use call spakwa call spakin sub #200 ,sp ; allocate a buffer mov sp ,r4 ; point to the buffer clr -(sp) ; count the total length setpar #SOH ,(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 25$ ; no mov #maxpak ,r0 ; yes, reset packet size please 25$: 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) mov 4(r5) ,r1 ; get the data length beq 40$ ; nothing to do mov 6(r5) ,r3 ; address of the data to send cmp r1 ,#maxpak ; too much data ? blos 30$ ; no mov #maxpak ,r1 ; yes, fix it please 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 movb conpar+p.eol,(r4)+ ; end of line needed ? inc @sp ; packetlength := succ(packetlength) mov (sp)+ ,r1 ; packet length mov sp ,r4 ; address(buffer) calls binwri ,; and dump the buffer out now call spakfi ; handle ibm stuff if possible 100$: add #200 ,sp ; pop the buffer unsave ; pop registers that we used return global .sbttl spack routines 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 binwri ,; 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 inc pcnt.s(r1) ; pakcount(type):=pakcount(type)+1 inc pcnt.s+0 30$: return 200$: .asciz /SPACK - / .even 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 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 200$: .byte msg$snd .byte msg$ser .byte msg$rcv .byte msg$command .byte msg$generic .byte 0 .even 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 .lsize = 22 ; 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 rpack$::save sub #.lsize ,sp ; allocate space for local data mov sp ,r4 ; and point to it please sub #200 ,sp ; allocate temp buffer for storing 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 call rpakin ; initialize things 10$: tst .done(r4) ; while not done do bne 90$ ; begin call rpakrd ; read(input,ch); bcs 95$ ; exit if error; clrpar r1 ; ch := ch and chr(177B); cmpb r1 ,#SOH ; if ch = SOH then resynch beq 80$ ; movb r1 ,(r3)+ ; checkpacket++ := ch unchar r1 ,r0 ; len := unchar(ch)-2-checksum_size sub #2 ,r0 ; sub chksiz ,r0 ; bge 15$ ; if datalength < 0 add chksiz ,r0 ; then dec r0 ; len := len + checksum_size - 1 movb #defchk ,chktyp ; checksum_type := default_type mov #1 ,chksiz ; checksum_len := 1 15$: mov r0 ,.len(r4) ; call rpakrd ; read(input,ch) bcs 95$ ; exit if error clrpar r1 ; ch := ch and chr(177B) cmpb r1 ,#SOH ; if ch = SOH then resynch beq 80$ ; movb r1 ,(r3)+ ; checkpacket++ := ch unchar r1 ,.num(r4) call rpakrd ; read(input,ch) bcs 95$ ; exit if error clrpar r1 ; ch := ch and chr(177B) cmpb r1 ,#SOH ; if ch = SOH then resynch beq 80$ ; movb r1 ,(r3)+ ; checkpacket++ := ch mov r1 ,.type(r4) ; packet_type := ch 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 ,#SOH ; if ch = SOH then resynch beq 80$ ; cmp .size(r4),#maxpak ; 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 call rpakst ; do stats and disk dumping now 100$: add #.lsize+200,sp ; pop local data + local buffer unsave return global .sbttl subroutines for RPACK only 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 ,#SOH ; 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 200$: .asciz // .even 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 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 inc pcnt.r(r1) ; simple inc pcnt.r+0 110$: bit #log$pa ,trace ; tracing today ? beq 120$ ; no calls dskdmp ,<#200$,.len(r4),.type(r4),.num(r4),@r5> 120$: return 200$: .asciz /RPACK - / .even 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 200$: .asciz /?Bad Checksum: rcv,calc are / .even 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 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$: 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 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 .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 @r1 ,(r2)+ ; add in the current totals clr (r1)+ ; and clear out the totals sob r0 ,10$ ; 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 @r1 ,(r2)+ ; sum in the total sent count clr (r1)+ ; and clear them out sob r0 ,20$ ; next please clr pcnt.n ; naks count unsave ; pop the registers we used return ; and exit global .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 waitsoh:clr r1 ; 10$: cmpb r1 ,#soh ; wait for a packet header please beq 100$ ; ok, exit call settmo ; get proper timeout set up calls binrea ,<#lun.ti,r0> ; read with timeout clrpar r1 tst r0 ; did the read work ? bne 20$ ; oops, just exit then call rawio br 10$ 20$: clr r1 100$: return 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 ; bisb conpar+p.spsiz,r5 ; get the recievers maximum size sub #12 ,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 .sbttl compute the space required for a character to be sent ; input: 2(sp) the character sizeof: mov r0 ,-(sp) ; save a register please clr r0 ; avoid the dreaded sign extension bisb 4(sp) ,r0 ; get the character into a use place mov #1 ,4(sp) ; assume the character needs one tstb r0 ; need the top bit today? bpl 10$ ; no, exit tst do8bit ; are we doing 8bit crap? beq 10$ ; no inc 4(sp) ; yes, we will need two at least bicb #200 ,r0 ; and see if further prefixing is done 10$: cmpb r0 ,#37 ; control character, then needs two bhi 10$ ; ok, check for 8bit and prefixing inc 4(sp) ; controls need two 100$: mov (sp)+ ,r0 ; exit return gnc: mov #lun.in ,r0 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 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$ 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) 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 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 mov #erbfsiz,r2 ; length so far 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 prerrp::.print #200$ .print @r5 .newli clr logini return .enabl lc 200$: .asciz /Aborting with error from remote / .even 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 calls error ,<#1,#e$retr> ; send/print the error message 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 .if ndf r$$ias .save .endc ;r$$ias .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/ .even .if df r$$ias .psect $code .iff ;r$$ias .restore .endc ;r$$ias .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 .if gt r$$rms-1 ; If on RMS version 2 ... cmp r0 ,#ER$NMF ; no more files matching name ? beq 20$ ; yes, we are all done then .iff ;r$$rms-1 ; If on RMS version 1 ... .if gt r$$fcs ; If we are to have wild card support cmp r0 ,#ER$NMF ; No more files matching name ? beq 20$ ; yes, we are all done then .endc ;r$$fcs ; .endc ;r$$rms-1 ; 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 .if gt r$$rms-1 global .endc ;r$$rms-1 .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 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) .endc ; if ne, parlok .end