home *** CD-ROM | disk | FTP | other *** search
Text File | 1998-03-16 | 56.7 KB | 2,493 lines |
- .title KSERVE
- .enabl lc
- ;++
- ;
- ; Kermit console server.
- ;
- ; Allows KERMIT communication with a microcomputer
- ; connected in place of the CTY (TT:).
- ;
- ; By John Wilson.
- ;
- ; 23-Oct-88 Created.
- ; 04-Dec-90 Added directory command.
- ; 10-Dec-90 Receive (from us) command.
- ; 24-Jan-92 Generate attribute packets.
- ;
- ;--
- .mcall .close,.dstat,.exit,.fetch,.print
- .mcall .purge,.releas,.ttinr,.ttyout,.wait
- ;
- eis$$= 1 ;NZ => processor has EIS
- rt11$$= 1 ;NZ => OS is RT-11
- ;
- soh= 1 ;SOH character is ^A
- binlin= 0 ;NZ => we have an 8-bit line
- ;Z => 7-bit line, will need QBIN escape
- ;
- bufsiz= 4000 ;buffer size, in bytes
- ;
- .enter= emt+375
- .lookup=emt+375
- .rctrlo=emt+355
- .read= emt+375
- .readw= emt+375
- .write= emt+375
- ;
- .asect
- .= 44
- .word 50000 ;set LC, noecho bits in JSW
- .psect
- ;
- attr= 10 ;CAPAS bit for attribute packets
- ;
- lf= 12
- cr= 15
- ;
- start: ; gentlemen, start your engines!
- mov #<^RDK >,defdev ;initial default device
- clrb binfil ;not binary files
- ; init packet parameters
- movb #cr,eol ;init eol
- clrb npad ;no pads
- movb #77.,maxl ;MAXL=80. (-header/checksum)
- loop: clrb seq ;always packet 0 in command wait
- mov #1,lchk ;check type is 1 until SEND-INIT
- mov #chk1,checka
- call getpac ;get a packet
- bcc 10$ ;handle it
- call nak ;NAK it
- br loop ;loop
- 10$: movb r1,seq ;accept whatever they think we're at
- mov #cmds,r2 ;pt at table
- mov #ncmds,r3 ;# entries
- 20$: cmp r0,(r2)+ ;is this it?
- beq 30$ ;yes
- tst (r2)+ ;no, skip addr
- sob r3,20$ ;loop
- movb r0,pnsc ;save char
- mov #pns,r0 ;pt at string
- call err ;send error
- br loop ;loop
- 30$: call @(r2)+ ;go
- br loop ;loop
- ;
- cmds: .word 'G,genric ;GENERIC
- .word 'I,init ;INITIALIZE
- .word 'K,kcmd ;KERMIT command
- .word 'R,send ;RECEIVE-INIT
- .word 'S,receiv ;SEND-INIT
- ncmds= <.-cmds>/4
- .sbttl generic commands
- ;+
- ;
- ; Generic commands (actual command in data field).
- ;
- ;-
- genric: ; unpack data field
- mov #buf1,bufptr ;set up ptr
- mov #80.,bufctr ;let's be reasonable
- jsr r1,iunpk ;unpack
- .word secrts ;don't flush
- bcs 10$ ;error
- mov #buf1,r5 ;init ptr
- mov bufptr,r4 ;calc length
- sub r5,r4
- beq 20$ ;0, who cares
- movb (r5)+,r0 ;get command byte
- dec r4 ;count it
- mov #gcmds,r2 ;pt at list
- mov #ngcmds,r3 ;number of entries
- 10$: cmp r0,(r2)+ ;is this it?
- beq 30$ ;yes
- tst (r2)+ ;skip address
- sob r3,10$ ;loop
- movb r0,cnsc ;save char
- mov #cns,r0 ;pt at string
- jmp err ;send error, return
- 20$: ; null packet
- jmp ack ;just ack it, ignore, return
- 30$: jmp @(r2)+ ;dispatch
- ;
- gcmds: .word 'C,cwd
- .word 'D,direct
- .word 'F,finish
- .word 'L,logout
- .word 'U,usage
- ngcmds= <.-gcmds>/4
- ;+
- ;
- ; Change working directory.
- ;
- ; For RT-11 V4.0 this will just mean set default device,
- ; since I don't know anything about the subdisks in V5.
- ;
- ;-
- cwd: tst r4 ;any data field?
- beq 20$ ;no
- mov #buf1,bufptr ;set up ptr
- mov #80.,bufctr ;good length
- jsr r1,iunpk ;unpack
- .word secrts ;don't flush
- bcs 40$ ;error
- ; parse it
- mov #buf1,r5 ;ptr
- movb (r5)+,r4 ;get 2nd length byte
- sub #40,r4 ;unchar()
- beq 20$ ;length byte is 0, skip
- add r5,r4 ;skip to end
- clrb (r4) ;.asciz
- call rad50 ;parse
- tst r0 ;end?
- beq 10$ ;yes
- cmp r0,#': ;must be colon
- bne 40$ ;no
- tstb (r5) ;followed by end?
- bne 40$ ;no
- 10$: ; see if it's a valid dev name
- mov r1,fbuf ;save dev name
- .dstat #dstat,#fbuf ;see if it's OK
- bcs 40$ ;nope
- mov fbuf,defdev ;it's the new default
- br 30$ ;skip
- 20$: mov #<^RDK >,defdev ;set it back to DK:
- 30$: ; echo back the name
- mov #ddev,r5 ;pt at string
- mov #ddev1,r4 ;pt at dev name
- mov defdev,r1 ;get name
- call r50nbl ;convert it
- movb #':,(r4)+ ;colon
- movb #cr,(r4)+ ;crlf
- movb #lf,(r4)+
- sub r5,r4
- call ldatn ;make data field
- call ldatf ;fix r4, r5
- jmp ack1 ;ack, return
- 40$: mov #bdn,r0 ;bad device name
- jmp err
- ;+
- ;
- ; Directory listing.
- ;
- ;-
- direct: movb #1,dirall ;assume we're showing everything
- clrb dirnon ;actually show it
- mov defdev,wlddev ;default device
- tst r4 ;filespec given?
- beq 40$ ;no
- ; unpack data field
- mov #buf1,bufptr ;set up ptr
- mov #80.,bufctr ;let's be reasonable
- jsr r1,iunpk ;unpack
- .word secrts ;don't flush
- bcs 10$ ;error
- ; parse it
- mov #buf1,r5 ;ptr
- movb (r5)+,r4 ;get 2nd length byte
- sub #40,r4 ;unchar()
- beq 40$ ;length byte is 0, skip
- add r5,r4 ;skip to end
- clrb (r4) ;.asciz, r4 is NZ for PWILD
- call pwild ;parse wildcard
- bcc 30$ ;skip if OK
- 10$: ; invalid
- mov #bfs,r0 ;bad file spec
- jmp err ;send error packet, return
- 20$: rts pc
- 30$: clrb dirall ;we're matching some wildcard
- 40$: ; do the SEND-INIT thing
- call iparms ;init parms
- call sparms ;prepare ours
- mov #'S,r0 ;SEND-INIT
- call makpac ;make a packet
- call sndack ;send it, get ACK
- bcs 20$ ;punt
- call rparms ;get their parms
- call fparms ;finish up
- ; send a blank text header
- mov #'X,r0 ;type
- call sndsmp ;send it
- bcs 20$ ;error
- ; set up for dir read
- call dirini ;init dir read
- bcs 120$ ;err, punt
- mov #txbuf+3,-(sp) ;init LDAT parms
- movb maxl,r0
- mov r0,-(sp)
- 50$: ; process next segment
- call dirseg ;get next seg
- bcs 110$ ;err or end
- 60$: ; display next file
- mov #buf2,r4 ;output line buf
- mov (r5)+,r1 ;convert first word
- beq 50$ ;end of seg, get next
- call r50
- mov (r5)+,r1 ;2nd word
- call r50
- movb #'.,(r4)+ ;point
- mov (r5)+,r1 ;extension
- call r50
- mov #10,r3 ;column counter
- add r3,r4 ;skip past end of field
- mov (r5)+,r1 ;get length of file
- mov #10.,r2 ;radix
- 70$: clr r0 ;0-extend
- div r2,r0 ;divide
- bis #'0,r1 ;convert remainder
- movb r1,-(r4) ;save
- dec r3 ;count it
- mov r0,r1 ;copy
- bne 70$ ;loop if there's more
- 80$: movb #' ,-(r4) ;pad with blanks
- sob r3,80$ ;loop
- add #10,r4 ;skip to end of field
- mov (r5)+,r3 ;get date
- beq 100$ ;meaningless, never mind
- movb #' ,(r4)+ ;2 more blanks
- movb #' ,(r4)+
- mov r3,r1 ;copy date
- ash #-5,r1 ;right 5
- bic #^C37,r1 ;isolate low 5
- call dec2 ;day as 2-dig decimal
- mov r3,r1 ;save
- swab r3 ;put month in low byte
- bic #^C74,r3 ;isolate month*4
- add #months-4,r3 ;index to -Month-
- mov #5,r0 ;count
- 90$: movb (r3)+,(r4)+ ;copy a byte
- sob r0,90$ ;loop
- bic #^C37,r1 ;isolate year
- add #72.,r1 ;what's so special about 1972?
- cmp r1,#100. ;they should have just kept
- blo .+6 ;the last 2 digs of year (7 bits)
- sub #100.,r1 ;handle 2000+ AD (ha!)
- call dec2 ;convert
- 100$: movb #cr,(r4)+ ;crlf
- movb #lf,(r4)+
- ; send this line to the toy computer
- mov (sp)+,r2 ;restore LDAT parms
- mov (sp)+,r1
- mov r5,-(sp) ;save dir ptr
- mov #buf2,r5 ;begn of line
- sub r5,r4 ;length
- call sdat ;send data
- mov (sp)+,r5 ;[restore r5]
- bcs 220$ ;punt
- mov r1,-(sp) ;save
- mov r2,-(sp)
- br 60$ ;loop
- 110$: ; end or error
- beq 130$ ;end, skip
- add #4,sp ;flush stack
- 120$: mov #ioerr,r0 ;pt at msg
- jmp err ;bitch, return
- 130$: ; end of listing
- call dirsum ;get dir summary
- ; send summary to the toy computer
- mov (sp)+,r2 ;restore LDAT parms
- mov (sp)+,r1
- call sdat ;send data
- bcs 210$ ;punt
- mov #txbuf+3,r5 ;pt at buf
- mov r1,r4 ;copy end
- sub r5,r4 ;find it
- beq 200$ ;none
- mov #'D,r0 ;packet type
- call makpac ;make it
- call sndack ;send, get ACK
- bcs 210$ ;punt
- 200$: mov #'Z,r0 ;end of file
- call sndsmp
- bcs 210$ ;punt
- mov #'B,r0 ;break
- call sndsmp ;(don't worry about errors)
- 210$: rts pc
- 220$: ; retry limit reached, punt
- .close #0 ;close dir
- rts pc
- ;
- .enabl lsb
- 10$: ; flush output buffer
- mov r5,-(sp) ;save buf posn
- mov r4,-(sp)
- mov #txbuf+3,r5 ;pt at buf
- mov r1,r4 ;copy end
- sub r5,r4 ;find it
- mov #'D,r0 ;packet type
- call makpac ;make a packet
- call sndack ;send it, get ACK
- bcs 20$ ;failed, skip
- mov (sp)+,r4 ;restore
- mov (sp)+,r5
- mov #txbuf+3,r1 ;reinit
- movb maxl,r2
- sdat: call ldat ;continue loading
- bcs 10$ ;full again, loop
- 20$: rts pc
- .dsabl lsb
- ;
- dec2: ; number in r1 to convert 2-digit decimal
- clr r0 ;0-extend
- div #10.,r0 ;divide
- bis #'0,r0 ;convert high dig
- movb r0,(r4)+
- bis #'0,r1 ;convert low dig
- movb r1,(r4)+
- rts pc
- ;
- decv: ; convert variable-width decimal no. in r1
- cmp r1,#10. ;do we need to recurse?
- blo 10$ ;no
- clr r0 ;0-extend
- div #10.,r0 ;divide
- mov r1,-(sp) ;save remainder
- mov r0,r1 ;copy quotient
- call decv ;recurse
- mov (sp)+,r1 ;restore remainder
- 10$: bis #'0,r1 ;convert
- movb r1,(r4)+ ;save
- rts pc
- ;+
- ;
- ; Convert a radix-50 word to a 3-character ASCII string.
- ;
- ; r1 word
- ; r4 buffer ptr
- ;
- ;-
- r50: clr r0 ;0-extend
- div #50,r0 ;divide
- mov r1,r2 ;save remainder
- mov r0,r1 ;copy
- clr r0 ;0-extend
- div #50,r0 ;divide
- movb r50t(r0),(r4)+ ;first char
- movb r50t(r1),(r4)+ ;second
- movb r50t(r2),(r4)+ ;third
- rts pc
- ;+
- ;
- ; Finish/logout.
- ;
- ; ACK and kill the server.
- ;
- ;-
- finish:
- logout: call ack ;ACK it
- .exit ;bye
- ;+
- ;
- ; Disk usage.
- ;
- ;-
- usage: mov defdev,wlddev ;copy dev name
- movb #1,dirall ;look at all files
- movb #1,dirnon ;but don't bother making a list
- call dirini ;init dir I/O
- bcs 20$ ;err
- 10$: call dirseg ;scan next segment
- bcc 10$ ;loop until all done
- bne 20$ ;err
- call dirsum ;make dir summary
- ; send to toy computer
- call ldatn ;load data field
- call ldatf ;fix for ACK1
- jmp ack1 ;ACK, return
- 20$: ; I/O error
- mov #ioerr,r0 ;point
- jmp err ;bitch, return
- ;+
- ;
- ; Make a summary of a directory scan.
- ;
- ; On return:
- ; r5 ptr to line (#buf2)
- ; r4 length
- ;
- ;-
- dirsum: mov #buf2,r4 ;pt at buf2
- ; display # of files
- mov files,r1 ;print # files
- call decv
- mov #tfile,r0 ;string
- 10$: movb (r0)+,(r4)+ ;copy
- bne 10$
- dec r4
- dec files ;files=1?
- beq 20$ ;yes
- movb #'s,(r4)+ ;s
- 20$: ; display # of blks used
- movb #',,(r4)+ ;,
- movb #' ,(r4)+
- mov used,r1 ;print # blks in use
- call decv
- mov #tblk,r0 ;string
- 30$: movb (r0)+,(r4)+ ;copy
- bne 30$
- dec r4
- dec used ;used=1?
- beq 40$ ;yes
- movb #'s,(r4)+ ;s
- 40$: movb (r0)+,(r4)+ ;copy " in use"
- bne 40$
- dec r4
- tstb dirall ;showing frees too?
- beq 60$ ;no
- ; display # of free blks
- movb #',,(r4)+ ;,
- movb #' ,(r4)+
- mov free,r1 ;print # free blks
- call decv
- mov #tfree,r0 ;string
- 50$: movb (r0)+,(r4)+ ;copy
- bne 50$
- dec r4
- 60$: movb #cr,(r4)+ ;crlf
- movb #lf,(r4)+
- mov #buf2,r5 ;begn of line
- sub r5,r4 ;length
- rts pc
- ;
- .sbttl initialize parameters
- ;+
- ;
- ; Takes parms as usual, responds with ours.
- ;
- ;-
- init: call iparms ;init parm negotiation
- call rparms ;process the ones we got
- call sparms ;set up the ones to send
- call ack1 ;send them
- jmp fparms ;finish up, return
- .sbttl kermit command
- ;+
- ;
- ; Handle what would normally be keyboard commands.
- ;
- ;-
- kcmd: mov #buf1,bufptr ;set up ptr
- mov #132.,bufctr ;good length
- clrb buf1 ;start with nothing
- tst r4 ;is that all there is?
- beq 10$ ;yep
- jsr r1,iunpk ;unpack
- .word secrts ;don't flush
- bcs 20$ ;error
- clrb @bufptr ;zap end
- 10$: mov #buf1,r5 ;ptr
- mov #cmdtab,r4 ;pt at table
- call parskw ;look up keyword
- bcs what ;error
- jmp ack ;null, just ACK
- 20$: mov #toolng,r0 ;pt at msg
- jmp err ;punt, return
- ;+
- ;
- ; Echo back the keyword we didn't understand.
- ;
- ;-
- what: bcc 20$ ;keyword was just missing
- mov #buf1,r5 ;pt at buf
- mov r5,r4 ;copy
- 10$: movb (r3)+,(r4)+ ;copy keyword
- sob r2,10$
- movb #'?,(r4)+ ;huh?
- movb #'?,(r4)+
- movb #cr,(r4)+ ;eol
- movb #lf,(r4)+
- sub r5,r4 ;find length
- call ldatn ;make a packet
- call ldatf ;get length
- mov #'E,r0 ;packet type
- call makpac ;make packet
- jmp putpac ;send it
- 20$: mov #mkw,r0 ;missing keyword
- jmp err
- ;
- cmdtab: .asciz <2>/SET/<0>
- .word set
- ; .asciz <2>/SHOW/
- ; .word show
- .word 0
- ;+
- ;
- ; Set stuff.
- ;
- ;-
- set: mov #settab,r4 ;pt at table
- call parskw ;parse a keyword
- br what ;complain
- ;
- settab: .asciz <1>/FILE/
- .word setfil
- .word 0
- ;+
- ;
- ; Set file.
- ;
- ;-
- setfil: mov #stftab,r4 ;pt at table
- call parskw ;parse a keyword
- br what ;complain
- ;
- stftab: .asciz <1>/TYPE/
- .word stftyp
- .word 0
- ;+
- ;
- ; Set file type.
- ;
- ;-
- stftyp: mov #ftptab,r4 ;pt at table
- call parskw ;parse a keyword
- br what ;complain
- ;
- ftptab: .asciz <1>/BINARY/
- .word setbin
- .asciz <1>/TEXT/
- .word settxt
- .word 0
- ;+
- ;
- ; Set file type binary.
- ;
- ;-
- setbin: movb #377,binfil ;yep
- jsr r5,reply ;reply
- .asciz /Binary mode set. All bytes will be transferred./<cr><lf>
- .even
- ;+
- ;
- ; Set file type text.
- ;
- ;-
- settxt: clrb binfil ;yep
- jsr r5,reply ;reply
- .asciz /Text mode set. Trailing nulls will be stripped./<cr><lf>
- .even
- ;+
- ;
- ; Send a reply.
- ;
- ; Called through r5 with in-line .asciz string.
- ;
- ;-
- reply: tst (sp)+ ;lose old r5
- mov r5,r4 ;copy
- 10$: tstb (r4)+ ;count
- bne 10$
- dec r4 ;back up
- sub r5,r4 ;find length
- call ldatn ;build data field
- call ldatf ;set up r4, r5
- jmp ack1 ;send reply, return
- ;+
- ;
- ; Parse a keyword and dispatch on it.
- ;
- ; r5 ptr to current posn in .asciz string
- ; r4 ptr to dispatch table
- ;
- ; If the keyword is OK, we flush the return addr and
- ; jump to the routine. Otherwise we return C=1.
- ; We return C=0 if there was nothing left on the line.
- ;
- ;-
- parskw: movb (r5)+,r0 ;get next char
- beq 100$ ;eol
- cmp r0,#<' > ;blank or cc?
- blos parskw ;yes, ignore
- 10$: dec r5 ;back up
- mov r5,r3 ;copy
- 20$: movb (r5)+,r0 ;get next char
- beq 30$ ;eol
- cmp r0,#<' > ;blank or cc?
- blos 30$ ;yes
- cmp r0,#'a ;lower case?
- blo 20$ ;no
- cmp r0,#'z
- bhi 20$
- bic #40,r0 ;convert
- movb r0,-1(r5)
- br 20$
- 30$: dec r5 ;back up
- mov r5,r2 ;copy
- sub r3,r2 ;find length
- 40$: ; search dispatch table for string
- movb (r4)+,r0 ;get min length to match
- beq 90$ ;end of list
- cmpb r2,r0 ;long enough to match?
- blo 80$ ;no, skip
- mov r3,r1 ;copy addr
- mov r2,r0 ;and len
- 50$: cmpb (r1)+,(r4)+ ;same?
- bne 70$ ;no
- sob r0,50$ ;loop
- 60$: tstb (r4)+ ;skip to end of string
- bne 60$ ;loop
- inc r4 ;round to even
- bic #1,r4
- tst (sp)+ ;toss return addr
- jmp @(r4)+ ;dispatch
- 70$: dec r4 ;might have been end of string
- 80$: tstb (r4)+ ;skip to end
- bne 80$ ;loop
- add #3,r4 ;+2, round to even
- bic #1,r4
- br 40$ ;loop
- 90$: sec ;invalid
- rts pc
- 100$: clc ;eol
- rts pc
- .sbttl receive a file
- ;+
- ;
- ; Receive a file from the toy computer.
- ;
- ;-
- receiv: call iparms ;init parm negotiation
- call rparms ;process theirs
- call sparms ;prepare ours
- call ack1 ;send them
- call fparms ;finish up
- 10$: ; start next file
- call getpac ;get a packet
- bcc 20$ ;got it
- ;;;;; heuristic:
- ; if we just ACKed with our parameters, and changed CHKT to
- ; something other than '1, see if this packet would seem good
- ; if it were a SEND-INIT with CHKT=1. if so, re-ACK with CHKT=1.
- ;;; we'll have to make sure GETPAC actually read a whole
- ;;; packet and that the checksum was the only problem.
- call nak ;nope
- br 10$ ;loop
- 20$: cmpb r1,seq ;current packet?
- bne 30$ ;no, must be previous
- cmp r0,#'F ;FILE-HEADER?
- beq 50$ ;yes
- cmp r0,#'B ;BREAK?
- bne 40$ ;no, skip
- jmp ack ;ACK it and return to loop
- 30$: call reack ;re-ACK
- br 10$ ;try again
- 40$: ; protocol violation
- movb r0,pvlc ;save char
- mov #pvl,r0 ;pt at string
- jmp err ;error packet
- 50$: ; starting a new file
- mov #buf1,bufptr ;set up for unpacking
- mov #bufsiz-1,bufctr ;allow for ^@ at end
- jsr r1,iunpk ;unpack
- .word secrts ;don't flush
- clrb @bufptr ;mark end
- mov #buf1,r5 ;pt at filename
- call file ;parse filename, get handler
- bcc 60$ ;skip
- mov #bfs,r0 ;bad filespec
- jmp err ;bitch, return
- 60$: ; set up for file output
- mov #buf2,wca ;set core addr
- mov #bufsiz/2,wwc ;word count
- clr wblk ;init blk #
- mov #buf1,cbuf ;current buf
- mov #buf1,bufptr ;pointer
- mov #bufsiz,bufctr ;and free count
- ; return the file name we're using
- mov #buf2,r4 ;output line buf
- mov #fbuf,r5
- mov (r5)+,r1 ;dev:
- call r50nbl
- movb #':,(r4)+
- mov (r5)+,r1 ;filename
- call r50nbl
- mov (r5)+,r1
- call r50nbl
- movb #'.,(r4)+ ;.
- mov (r5)+,r1 ;ext
- call r50nbl
- mov #buf2,r5 ;pt
- sub r5,r4 ;find length
- call ldatn ;encode
- call ldatf ;fix for ACK1
- call ack1 ;ACK, give filename
- 70$: ; slurp attribute packet(s)
- call getpac ;get a packet
- bcs 80$
- cmpb r1,seq ;is this curr pkt?
- bne 90$ ;no, must be previous
- cmp r0,#'A ;attribute packet?
- bne 100$ ;no
- ; handle attributes
-
- ;;;;;;;;;;;
-
- call ack ;ACK
- br 70$
- 80$: call nak ;NAK it
- br 70$ ;try again
- 90$: call reack ;re-ACK previous pkt
- br 70$
- 100$: ; not 'A packet, open file
- mov r0,-(sp) ;save
- call ldev ;make sure we have the dev handler
- bcs 120$ ;shouldn't happen
- mov #earea,r0 ;point at it
- .enter ;open the file
- mov (sp)+,r0 ;[restore]
- bcs 110$ ;error
- clr -(sp) ;initial flags
- mov #1,-(sp) ;initial repeat count
- br 150$ ;groovy, go see if it was 'D or 'Z
- 110$: mov #ucf,r0 ;unable to create file
- br 130$
- 120$: mov #bdn,r0 ;bad device name
- 130$: jmp err ;bitch, return
- 140$: ; read (another) data packet
- call getpac ;get a packet
- bcs 160$ ;bad, skip
- cmpb r1,seq ;is this curr pkt?
- bne 170$ ;no, must be previous
- 150$: cmp r0,#'D ;data?
- beq 180$
- cmp r0,#'Z ;eof?
- beq 190$
- mov r0,r1 ;save
- .purge #1 ;reset the file
- add #4,sp ;flush stack
- cmp r0,#'E ;error packet?
- beq .+6
- jmp 40$ ;no, protocol violation
- rts pc ;gracefully punt
- 160$: ; bad checksum or timeout
- call nak ;nak it
- br 140$ ;more
- 170$: ; they resent the previous packet
- call reack ;re-ACK previous packet
- br 140$ ;more
- 180$: ; data packet
- mov r4,-(sp) ;save length & ptr
- mov r5,-(sp)
- call ack ;ACK the packet
- mov (sp)+,r5 ;restore
- mov (sp)+,r4
- beq 140$ ;length=0, ignore
- mov (sp)+,r3 ;restore flags
- mov (sp)+,r2
- jsr r1,unpack ;unpack packet
- .word wrbuf ;flush routine
- bcs 220$ ;flush error
- mov r2,-(sp) ;save flags
- mov r3,-(sp)
- br 140$ ;get next packet
- 190$: ; eof, flush buffer and close file
- tst r4 ;is there a data field in the Z packet?
- beq .+4
- movb (r5),r4 ;get 1st char
- mov r4,-(sp) ;save
- call ack ;ACK the ^Z
- mov (sp)+,r4 ;restore
- mov #bufsiz,r0 ;find # bytes in buf
- sub bufctr,r0
- beq 200$ ;none, skip
- inc r0 ;round up
- asr r0 ;/2=wc
- mov r0,wwc ;save
- clrb @bufptr ;zap odd byte, if any (at least 1 byte free)
- mov cbuf,wca ;core addr
- .wait #1 ;finish previous
- mov #warea,r0 ;EMT area
- .write ;write last buffer
- bcs 210$ ;error
- 200$: add #4,sp ;purge stack
- cmp r4,#'D ;delete the file?
- bne 230$ ;no
- ; Z/D, delete the file (user aborted or something)
- .purge #1 ;purge the file
- jmp 10$ ;start next
- 210$: ; error writing file
- add #4,sp ;flush stack
- 220$: .purge #1 ;purge the file
- mov #werr,r0 ;pt at string
- jmp err ;send, return
- 230$: ; keep the file
- .close #1 ;close the file
- ;;; now's the time to apply 'A packets and set the date etc.
- jmp 10$ ;start next
- ;
- wrbuf: ; flush buffer
- .wait #1 ;wait for previous transfer
- mov wca,r0 ;get previous buf addr
- mov cbuf,wca ;reset to current
- mov r0,cbuf ;prev is now current
- mov r0,bufptr ;set ptr
- mov #bufsiz,bufctr ;and counter
- mov #warea,r0 ;queue a write
- .write
- bcs 10$ ;just punt if C=1
- add #bufsiz/1000,wblk ;update blk #, C=0
- 10$: rts pc
- ;
- secrts: ; dummy flush routine for IUNPK/UNPACK
- sec ;flush failed
- rts pc
- .sbttl send file(s)
- ;+
- ;
- ; Send file(s) to the toy computer.
- ;
- ;-
- send: tst r4 ;filespec given?
- beq 20$ ;no
- ; unpack data field
- mov #buf1,bufptr ;set up ptr
- mov #80.,bufctr ;let's be reasonable
- jsr r1,iunpk ;unpack
- .word secrts ;don't flush
- bcs 10$ ;error
- ; parse it
- clrb @bufptr ;zap end
- mov #buf1,r5 ;pt at string
- clr r4 ;no weird defaults
- call pwild ;parse wildcard
- bcc 20$ ;skip if OK
- 10$: ; invalid
- mov #bfs,r0 ;bad file spec
- jmp err ;send error packet, return
- 20$: ; set up for dir lookup
- clrb dirall ;we aren't showing everything
- clrb dirnon ;but give me the filenames
- ;;; don't bother with any of this if it's a char device
- call dirini ;get psyched
- bcs 60$ ;error opening dev
- ; make sure at least 1 match exists
- 30$: call dirseg ;get next segment
- bcs 40$ ;error
- tst (r5) ;anything?
- beq 30$ ;no, try next seg
- br 80$ ;OK, skip
- 40$: bne 60$ ;I/O err
- ; file not found
- tstb wldflg ;were we in a wildcard search?
- beq 50$ ;no
- mov #nomtch,r0 ;no matches found
- br 70$
- 50$: mov #fnf,r0 ;file not found
- br 70$
- 60$: ; I/O error
- mov #ioerr,r0
- 70$: jmp err ;later
- 80$: ; do the SEND-INIT thing
- mov r5,-(sp) ;save file ptr
- call iparms ;init parms
- call sparms ;prepare ours
- mov #'S,r0 ;SEND-INIT
- call makpac ;make a packet
- call sndack ;send it, get ACK
- bcs 130$ ;punt
- call rparms ;get their parms
- call fparms ;finish up
- mov (sp)+,r5 ;recover r5
- br 100$ ;go send first file
- 90$: ; handle next dir segment
- call dirseg ;get next
- bcc 100$ ;OK
- bne 60$ ;I/O error
- mov #'B,r0 ;break transmission
- jmp sndsmp ;tell them, return (ignore err)
- 100$: ; handle next file
- mov #fbuf+2,r3 ;.LOOKUP buf
- mov #buf2,r4 ;output line buf
- mov (r5)+,r1 ;convert first word
- beq 90$ ;end of seg, get next
- mov r1,(r3)+ ;save in fbuf
- call r50nbl
- mov (r5)+,r1 ;2nd word
- mov r1,(r3)+
- call r50nbl
- movb #'.,(r4)+ ;point
- mov (r5)+,r1 ;extension
- mov r1,(r3)
- call r50nbl
- mov r5,-(sp) ;save r5
- mov #buf2,r5 ;pt
- sub r5,r4 ;find length
- ; open the file
- mov #larea,r0 ;pt at area
- ;;; mov #wlddev,2(r0) ;;;;;;; open the whole device
- .lookup ;try to open file for input
- bcs 120$ ;guess not
- ; OK, send FILE-HEADER packet
- call ldatn ;go
- call ldatf ;fix
- mov #'F,r0 ;FILE-HEADER
- call makpac ;make packet
- call sndack ;send it
- bcs 130$ ;punt
- ; should we send file attributes?
- ;; br 110$ ;;; no attributes when sending whole device
- ;;; is it a char dev?
- ;;; b<yes> 110$ ;don't send attr pack
- bitb #attr,capas ;sending attribute packets?
- bne 140$ ;yes
- 110$: add #4,(sp) ;no, skip size and date
- br 160$ ;go send file
- 120$: ; .LOOKUP error
- mov #uof,r0 ;unable to open file
- tst (sp)+ ;flush r5
- jmp err ;later
- 130$: ; retry limit reached, punt quietly
- tst (sp)+ ;lose dir tab ptr
- rts pc ;timed out
- 140$: ; send ATTRIBUTE packet
- mov (sp),r5 ;get ptr
- ; size in K
- mov #txbuf+3,r4 ;init ptr, skip size
- movb #'!,(r4)+ ;length
- inc r4 ;skip length of length
- mov (r5)+,r1 ;get file size
- add #1,r1 ;round up, C=0 (or 1 if 200000)
- ror r1 ;(blks+1)/2 = K bytes
- call decv ;convert (r4=txbuf+3+2)
- mov r4,r0 ;copy
- sub #txbuf+3+2-40,r0 ;find char(width of field)
- movb r0,txbuf+3+1 ;poke it back
- ; date of creation
- mov (r5)+,r3 ;get date
- beq 150$ ;no date, don't send any
- movb #'#,(r4)+ ;date [& time - RT doesn't save times]
- movb #8.+40,(r4)+ ;length=8.
- mov r3,r1 ;copy date
- bic #^C37,r1 ;isolate year
- mov r3,r0 ;copy again (include RT V5 32s bit)
- ash #-10.,r0 ;shift b15 to b5
- bic #^C40,r0 ;isolate
- bis r0,r1 ;OR it in
- add #1972.,r1 ;origin is 1972
- call decv ;convert it (will always be 4 digits)
- mov r3,r1 ;copy date again
- ash #-10.,r1 ;right 10.
- bic #^C17,r1 ;isolate month
- call dec2 ;convert
- mov r3,r1 ;copy yet again
- ash #-5,r1 ;right 5
- bic #^C37,r1 ;isolate day
- call dec2 ;convert
- 150$: ; machine/OS
- movb #'.,(r4)+ ;machine/OS
- movb #2+40,(r4)+ ;length=2
- movb #'D,(r4)+ ;DEC
- .iif ne rt11$$, movb #'B,(r4)+ ;PDP-11/RT-11
- ; send it
- mov r5,(sp) ;update
- mov #txbuf+3,r5 ;pt at begn
- sub r5,r4 ;length
- mov #'A,r0 ;type
- call makpac ;build the packet
- call sndack ;send it
- bcs 130$ ;punt
- ; skip this file if they refused it
- tst r4 ;OK?
- beq 160$ ;yep
- cmpb (r5),#'Y ;OK?
- bne 260$ ;no, do next file
- 160$: ; read initial bufferload
- clr rblk ;start at begn
- mov #bufsiz/2,rwc ;initial wc
- mov #buf1,rca ;initial buf
- mov #buf2,cbuf ;next buf
- mov #rarea,r0 ;read begn of file
- .read ;do it
- bcc 170$ ;skip if OK
- tst r0 ;err=read from EOF?
- beq 250$ ;yes, null file, send ^Z
- ;;; br 230$ ;;;;;; don't care if whole dev
- br 230$ ;no, I/O error
- 170$: mov r0,rlen ;so we know what to expect
- mov #txbuf+3,r1 ;for LDAT
- movb maxl,r2
- 180$: ; swap buffers
- mov rlen,r4 ;get # words expected
- beq 240$ ;eof, skip
- asl r4 ;# bytes
- .wait #1 ;wait for next buffer to fill
- mov rca,r5 ;pt at this buf
- mov cbuf,rca ;old curr buf will be next buf
- mov r5,cbuf ;next buf is now curr buf
- add #bufsiz/1000,rblk ;update blk #
- mov #rarea,r0 ;start next buffer reading
- .read ;do it
- bcc 190$ ;OK
- tst r0 ;rd from eof?
- ;;;;; sending whole dev, don't care
- ;; clr r0 ;;;;;;
- bne 230$ ;no, I/O error
- ;;;;
- 190$: mov r0,rlen ;# words expected
- tstb binfil ;binary file?
- bne 210$ ;yes
- ; scan off trailing nulls
- mov r4,r3 ;copy length
- add r5,r3 ;pt past end of blk
- 200$: tstb -(r3) ;back 1
- bne 210$ ;skip
- sob r4,200$ ;loop
- br 180$ ;all nulls, loop
- 210$: ; send next buffer
- call ldat ;convert
- bcc 180$ ;it fit, loop
- mov r4,-(sp) ;save input ptr
- mov r5,-(sp)
- call ldatf ;get addr, len
- mov #'D,r0 ;DATA packet
- call makpac ;build it
- call sndack ;send it, get ACK (C set)
- mov (sp)+,r5 ;[restore]
- mov (sp)+,r4
- mov #txbuf+3,r1 ;[init for next packet]
- movb maxl,r2
- bcc 210$ ;(C set by SNDACK) around for more
- 220$: ; too many retries
- .close #1 ;close file
- tst (sp)+ ;lose r5
- rts pc
- 230$: ; read error
- .close #1 ;close
- tst (sp)+ ;lose r5
- mov #rerr,r0 ;err msg
- jmp err
- 240$: ; end of file, flush last packet
- call ldatf ;get addr, len
- tst r4 ;anything?
- beq 250$ ;no
- mov #'D,r0 ;DATA packet
- call makpac ;build it
- call sndack ;send it, get ACK
- bcs 220$ ;oh well nice try
- 250$: ; send END-OF-FILE
- mov #'Z,r0 ;send END-OF-FILE
- call sndsmp
- bcs 220$ ;oh sure, NOW you wuss out
- 260$: .close #1 ;close the file
- mov (sp)+,r5 ;restore ptr
- jmp 100$ ;handle next file
- .sbttl file-related routines
- ;+
- ;
- ; Partially parse a wildcard and prepare for wildcard search.
- ;
- ; R5 ptr to .asciz string.
- ; R4 NZ => default filename/ext to * if missing,
- ; Z => each is blank if missing,
- ; *but* if the filename.ext is blank (except possibly
- ; for a device) then we write nothing either way.
- ;
- ; Return WLDDEV and WILD set up, device loaded (name at FBUF).
- ; C=1 wildcard contained invalid characters or bad format
- ; (two extensions, wildcard in device name, whatever)
- ;
- ; WLDFLG (byte) is set to non-zero (actually the # of wildcard chars)
- ; if the filespec actually is a wildcard. If WLDFLG=0, then it's
- ; just a filename, parse it with FILE.
- ;
- ;-
- pwild: clr wlddev ;no device yet
- 10$: mov #wild,r1 ;point at buf
- clr r2 ;no .'s yet
- clr r3 ;no wildcard chars either
- 20$: ; get next char
- movb (r5)+,r0 ;get a char
- beq 110$ ;end, skip
- cmp r0,#<' > ;blank?
- beq 20$ ;ignore
- cmp r0,#': ;device name?
- beq 90$ ;yes
- cmp r0,#'? ;RSTS-style wildcard?
- beq 70$ ;change to %
- cmp r0,#'a ;lower case?
- blo 30$ ;no
- cmp r0,#'z ;hm?
- bhi 30$ ;no
- bic #40,r0 ;yes, convert
- br 40$ ;we know char is OK
- 30$: ; make sure char is OK
- cmp r0,#<' > ;blank?
- beq 20$ ;yes, ignore
- cmp r0,#'. ;. is OK, once
- beq 50$
- cmp r0,#'% ;wildcards are OK
- beq 80$
- cmp r0,#'*
- beq 80$
- cmp r0,#'0 ;digits are OK
- blo 100$
- cmp r0,#'9
- blos 40$
- cmp r0,#'A ;letters are OK
- blo 100$
- cmp r0,#'Z
- bhi 100$
- 40$: movb r0,(r1)+ ;save
- br 20$ ;loop
- 50$: ; .
- tst r2 ;is this the first .?
- bne 100$ ;no
- tst r4 ;should we use default filename?
- beq 60$ ;no
- cmp r1,#wild ;is there any need?
- bne 60$ ;no
- movb #'*,(r1)+ ;yes, save it
- 60$: inc r2 ;set "." flag
- br 40$
- 70$: ; ? as wildcard (= %)
- movb #'%,r0 ;replace ? with %
- 80$: inc r3 ;wildcard
- br 40$ ;loop
- 90$: ; device name
- tst wlddev ;do we have one already?
- bne 100$ ;yes, error
- clrb (r1) ;mark end
- mov r5,-(sp) ;save ptr
- mov #wild,r5 ;pt at dev name
- call rad50 ;parse it
- mov (sp)+,r5 ;restore
- tst r0 ;stopped on nul?
- bne 100$ ;no, bad filename
- mov r1,wlddev ;save
- bne 10$ ;there was something
- mov defdev,wlddev ;set default anyway, don't allow ":DEV:"
- br 10$ ;get filename
- 100$: sec ;bad filename
- rts pc
- 110$: ; end of filespec
- tst wlddev ;did we ever get a device?
- bne 120$ ;yes
- mov defdev,wlddev ;no, use default
- 120$: ; make sure handler is loaded
- mov wlddev,fbuf ;copy
- call ldev ;load it
- bcs 140$ ;punt on err
- ; add ".*" to name if we're using default wildcards
- tst r4 ;should we add ".*" if no ext?
- beq 130$ ;no
- tst r2 ;was there an ext?
- bne 130$ ;yes
- cmp r1,#wild ;totally null name?
- beq 130$ ;yes, leave it alone
- movb #'.,(r1)+ ;.*
- movb #'*,(r1)+
- 130$: movb r3,wldflg ;remember whether it's a wildcard
- clrb (r1) ;C=0, mark end
- 140$: rts pc
- ;+
- ;
- ; Init for directory search.
- ;
- ; C=1 on directory open error.
- ;
- ;-
- dirini: clr free ;no free blks yet
- clr used ;no used blks either
- clr files ;and no files
- ; open disk non-file-structured to get dir
- mov #ludir,r0 ;open the device
- .lookup ;(non-file-structured)
- ; The directory should start at block 6, but SSM says that in case
- ; it's different the correct starting block no. should be read from
- ; the word at offset 724 in the home block (block 1).
- ; But, if the volume was initialized under RSTS/E by the FIT utility
- ; (like my SY:), this field is set to ASCII blanks.
- ; So, I'll hard code to block 6. Sorry.
- ; DIR.SAV 4.0 can read my SY: so it seems that it doesn't worry about
- ; home+724 either.
- mov #1,segnxt ;next seg will be #1
- rts pc
- ;+
- ;
- ; Process next segment of directory.
- ;
- ; On return:
- ; C=0 OK, MATLST contains 0-terminated list of files
- ; C=1 Z=1 no more dir segments (dir has been closed)
- ; C=1 Z=0 dir read error
- ;
- ; r5 pts to all the matches we found in this segment.
- ; There are up to 72. entries (the max possible # of file entries in a
- ; segment) of the following format:
- ; .rad50 /filnamext/
- ; .word size, date
- ;
- ; If DIRALL (byte) .ne.0, all files are copied (no wildcard comparison is
- ; performed), and empty blocks are copied as
- ; ".EMPTY." with no date.
- ;
- ; If DIRNON (byte) .ne.0, no files are copied. This is used to compute disk
- ; usage without bothering to copy all the filenames all over the place.
- ;
- ;-
- dirseg: mov segnxt,r0 ;get segment to read
- beq 110$ ;none, skip
- call getseg ;get it
- bcs 100$
- mov #matlst,-(sp) ;pt at match list
- 10$: ; process next directory entry
- mov (r5)+,r0 ;get status word
- bit #4000,r0 ;end of segment?
- bne 90$
- bit #1000,r0 ;empty block?
- bne 80$
- bit #2000,r0 ;permanent?
- beq 60$ ;no
- tstb dirnon ;showing nothing?
- bne 50$
- tstb dirall ;showing everything?
- bne 20$
- ; check this entry for wildcard match
- mov r5,-(sp) ;save
- mov #buf2,r4 ;pt at buf
- mov (r5)+,r1 ;convert filename
- call r50nbl
- mov (r5)+,r1
- call r50nbl
- movb #'.,(r4)+ ;.
- mov (r5),r1 ;extension
- call r50nbl
- clrb (r4)
- mov #wild,r5 ;pt at pattern
- mov #buf2,r4 ;test string
- call match ;match?
- mov (sp)+,r5 ;[restore]
- bcs 70$
- 20$: ; match, save this entry
- add 6(r5),used ;count as used
- inc files ;bump count
- 30$: mov (sp)+,r4 ;get ptr back
- mov (r5)+,(r4)+ ;copy filename
- mov (r5)+,(r4)+
- mov (r5)+,(r4)+ ;extension
- mov (r5)+,(r4)+ ;length
- tst (r5)+ ;skip tentative file info
- mov (r5)+,(r4)+ ;get date
- mov r4,-(sp) ;save
- 40$: add extbyt,r5 ;skip extra bytes, if any
- br 10$ ;loop
- 50$: add 6(r5),used ;count the file's blocks
- inc files ;count it
- br 70$ ;skip
- 60$: add 6(r5),free ;count tentative files as free
- 70$: ; skip this entry
- add #14,r5 ;skip
- br 40$
- 80$: ; < UNUSED > block
- add 6(r5),free ;update # free blks
- tstb dirall ;showing everything?
- beq 70$ ;no, skip this
- mov r5,r0 ;copy
- mov #<^R.EM>,(r0)+ ;.EMPTY.
- mov #<^RPTY>,(r0)+
- clr (r0)
- clr 6(r0) ;zap date
- br 30$ ;go display
- 90$: ; end of segment
- mov #matlst,r5 ;pt at match list
- mov (sp)+,r4 ;restore ptr
- clr (r4) ;mark end, C=0
- rts pc
- 100$: ; I/O error
- .close #0 ;close the dir
- clz ;Z=0
- sec ;C=1
- rts pc
- 110$: ; end of dir
- .close #0 ;close the dir
- +sec!sez ;C=1, Z=1 (no more segs)
- rts pc
- ;+
- ;
- ; Get dir segment in r0.
- ;
- ;-
- getseg: asl r0 ;*2
- add #4,r0 ;blks 6,7 are seg 1
- mov r0,dirblk ;copy ptr
- mov #rddir,r0 ;get (next) segment
- .readw ;read
- bcs 10$ ;bugged
- mov buf1+6,extbyt ;no. of extra bytes (FIT uses for RSTS RTSNAM)
- clr free ;no frees yet (C=0)
- mov buf1+2,segnxt ;save link to next
- mov #buf1+12,r5 ;pt at begn of seg
- 10$: rts pc
- ;+
- ;
- ; Check for a wildcard match.
- ;
- ; % matches exactly one character.
- ; * matches 0 or more characters.
- ;
- ; Wildcards may not span the ".".
- ;
- ; r5 .asciz /wildcard/
- ; r4 .asciz /name to check/
- ;
- ; C=0 if they matched, C=1 if not.
- ;
- ;-
- match: movb (r5)+,r0 ;get a char
- beq 20$ ;end of name
- cmp r0,#'% ;match one char?
- beq 30$ ;yes
- cmp r0,#'* ;match 0 or more chars?
- beq 40$ ;yes
- cmpb r0,(r4)+ ;same?
- beq match ;yes
- 10$: sec ;no
- rts pc
- 20$: tstb (r4) ;did both end at once? (C=0)
- bne 10$ ;no
- rts pc
- 30$: ; % match one character
- movb (r4)+,r0 ;get it
- beq 10$ ;end
- cmp r0,#'. ;don't skip to extension
- bne match
- br 10$
- 40$: ; * match 0 or more characters
- mov r5,-(sp) ;save
- mov r4,-(sp)
- call match ;recurse
- bcc 50$ ;got it
- mov (sp)+,r4 ;restore
- mov (sp)+,r5
- movb (r4)+,r0 ;skip a char
- beq 10$ ;lose
- cmp r0,#'. ;extension separator?
- beq 10$ ;yep, don't skip that
- br 40$ ;recurse
- 50$: add #4,sp ;flush stack (C=0)
- rts pc
- ;+
- ;
- ; Convert a radix-50 word to a 0- to 3-character ASCII string.
- ; Stop at first blank (all chars to right should be blank too).
- ;
- ; r1 word
- ; r4 buffer ptr
- ;
- ;-
- r50nbl: clr r0 ;0-extend
- div #50,r0 ;divide
- mov r1,r2 ;save remainder
- mov r0,r1 ;copy
- clr r0 ;0-extend
- div #50,r0 ;divide
- movb r50tnb(r0),(r4)+ ;first char
- beq 10$ ;whoops
- movb r50tnb(r1),(r4)+ ;second
- beq 10$
- movb r50tnb(r2),(r4)+ ;third
- beq 10$
- rts pc
- 10$: dec r4 ;back up
- rts pc
- ;+
- ;
- ; Parse a filename, save in FBUF.
- ;
- ; On entry:
- ; r5 source pointer
- ;
- ; C=1 if filename is bad.
- ;
- ;-
- file: mov #fbuf,r4 ;point at filename area
- mov defdev,(r4)+ ;set default device
- clr (r4)+ ;zap file & ext
- clr (r4)+
- clr (r4)
- sub #4,r4 ;back up to filename
- ; file or device name first
- call rad50 ;get it
- cmp r0,#': ;device?
- bne 10$ ;no
- mov r1,-2(r4) ;set it
- call rad50 ;get filename
- 10$: mov r1,(r4)+ ;it must be the filename
- mov r2,(r4)+
- cmp r0,#'. ;extension given?
- bne 20$ ;no
- call rad50 ;yes, eat it
- mov r1,(r4) ;save it
- 20$: ; r0 should be blank, tab or null here
- cmp r0,#<' > ;blank or ctrl char?
- bhi 30$ ;no, bugged
- clc ;OK
- rts pc
- 30$: sec ;error return
- rts pc
- ;+
- ;
- ; Parse a radix-50 string.
- ;
- ; r5 source pointer
- ;
- ; On return:
- ; r0 char we stopped on
- ; r1 1st 3 chars of string
- ; r2 2nd 3 chars of string
- ; r5 points to char in r0 +1
- ;
- ;-
- rad50: clr r1 ;init buf
- clr r2
- call chr50 ;get a char
- bcs 20$ ;yow
- asl r0 ;lookup 1st char
- mov rad50a(r0),r1 ;get it
- call chr50 ;get 2nd
- bcs 20$ ;end of string
- asl r0 ;lookup 2nd
- add rad50b(r0),r1
- call chr50 ;3rd
- bcs 20$
- add r0,r1
- call chr50 ;4th
- bcs 20$
- asl r0
- mov rad50a(r0),r2
- call chr50 ;5th
- bcs 20$
- asl r0
- add rad50b(r0),r2
- call chr50 ;6th
- bcs 20$
- add r0,r2
- 10$: call chr50 ;skip anything left
- bcc 10$
- 20$: rts pc
- ;+
- ;
- ; Get a char and cvt to radix 50 in r0.
- ;
- ; C=1 if we failed, char in r0.
- ;
- ;-
- chr50: movb (r5)+,r0 ;get it
- cmp r0,#<' > ;blank?
- beq chr50 ;yes, ignore
- cmp r0,#'0 ;digit?
- blo 10$
- cmp r0,#'9
- blos 20$
- cmp r0,#'A ;u.c. letter?
- blo 10$
- cmp r0,#'Z
- blos 30$
- cmp r0,#'a ;l.c. letter?
- blo 10$
- cmp r0,#'z
- blos 40$
- 10$: sec ;error return
- rts pc
- 20$: ; digit
- sub #'0-<^R 0>,r0 ;convert (C=0)
- rts pc
- 30$: ; upper case letter
- sub #'A-<^R A>,r0 ;convert (C=0)
- rts pc
- 40$: ; lower case letter
- sub #'a-<^R A>,r0 ;convert (C=0)
- rts pc
- ;+
- ;
- ; Make sure the device at FBUF is loaded.
- ;
- ; C=1 if invalid dev.
- ;
- ;-
- ldev: .dstat #dstat,#fbuf ;see if handler is loaded
- bcs 20$ ;invalid
- tst dstat+4 ;is it loaded?
- bne 20$ ;yes (C=0 from TST)
- ; device is non-resident, load it in
- tst device ;is there a device already?
- beq 10$ ;no
- .releas #device ;yes, release it
- 10$: .fetch #devhnd,#fbuf ;no, load it (set C)
- mov fbuf,device ;save device name
- 20$: rts pc
- ;
- .sbttl packet-level routines
- .rem $
-
- Packet format:
-
- +-----------------------------------+
- | soh | len | seq | typ | dat | chk |
- +-----------------------------------+
-
- soh = start-of-header character
- len = <length of seq through chk inclusive> +40
- seq = <sequence number mod 100> +40
- typ = type (ascii char)
- dat = data field (variable length, may be null)
- chk = 1, 2, or 3 byte checksum or CRC of len through dat inclusive
- $
- ;+
- ;
- ; Init SEND-INIT parms for negotiation.
- ;
- ;-
- iparms:
- .if ne binlin
- movb #'Y,mqbin ;QBIN is OK with me but not needed
- .iff
- movb #'&,mqbin ;QBIN not OK
- .endc
- clrb chkt ;CHKT not decided yet
- clrb mchkt ;I haven't voted either
- clrb rept ;no REPT char yet
- movb #'~,mrept ;I'd like to
- rts pc
- ;+
- ;
- ; Finish SEND-INIT parms processing.
- ;
- ;-
- fparms: ; make the CHKT change actually happen
- movb chkt,r0 ;get check type
- mov r0,lchk ;save length
- asl r0 ;*2
- mov checks-2(r0),checka ;look up routine to do checks
- ; fix MAXL to be max data field size
- movb maxl,r0 ;get MAXL
- sub #2,r0 ;don't count seq or typ
- sub lchk,r0 ;or checksum
- movb r0,maxl ;save
- rts pc
- ;+
- ;
- ; Prepare our SEND-INIT parms.
- ;
- ; Returns with:
- ; r5 data field
- ; r4 length
- ;
- ;-
- sparms: tstb mchkt ;have they specified MCHKT?
- bne 10$ ;yes
- movb #'1,mchkt ;no, my default is 1
- 10$: mov #mparms,r5 ;ptr
- mov #nmprms,r4 ;length
- rts pc
- ;+
- ;
- ; Process SEND-INIT parms received from them.
- ;
- ; On entry:
- ; r5 data field (with space for padding)
- ; r4 length
- ;
- ;-
- rparms: ; pad with blanks so we'll use defaults as appropriate
- mov #' ,r1 ;handy constant
- mov #nparms,r3 ;expected max length
- sub r4,r3 ;find # missing parms
- blos 20$ ;they must be a later version
- add r5,r4 ;pt at end
- 10$: movb r1,(r4)+ ;pad
- sob r3,10$
- 20$: ; read the parms
- mov #maxl,r4 ;point at param table
- ; MAXL=80.
- movb (r5)+,r0 ;get MAXL
- sub r1,r0 ;unchar()
- bne .+6 ;specified
- mov #80.,r0 ;default
- movb r0,(r4)+
- ; TIME=5
- movb (r5)+,r0 ;get TIME
- sub r1,r0 ;unchar()
- bne .+6 ;given
- mov #5,r0 ;def
- movb r0,(r4)+
- ; NPAD=0
- movb (r5)+,r0 ;get NPAD
- sub r1,r0 ;unchar()
- movb r0,(r4)+
- ; PADC=^@
- movb (r5)+,r0 ;get char
- asl r1 ;*2=100
- xor r1,r0 ;ctl()
- movb r0,(r4)+
- ; EOL=cr
- movb (r5)+,r0 ;get char
- asr r1 ;/2=40 again
- sub r1,r0 ;unchar()
- bne .+6 ;given
- mov #cr,r0 ;default
- movb r0,(r4)+
- ; QCTL=#
- movb (r5)+,r0 ;get char
- cmp r0,r1 ;given? (blank?)
- bne .+6 ;no
- movb #'#,r0 ;default
- movb r0,(r4)+
- ; QBIN=N
- movb (r5)+,r0 ;get char
- .if ne binlin
- movb #'N,mqbin ;assume they don't want to QBIN
- .endc
- cmp r0,r1 ;defaulted?
- beq 30$
- cmp r0,#'Y ;up to us?
- beq 30$
- cmp r0,#'N ;they don't want to?
- beq 30$ ;(we're screwed if BINLIN=0)
- movb r0,mqbin ;they want to, remember what
- br 40$ ;skip
- 30$: ; our decision, tell them what we've already assumed
- .if ne binlin
- clr r0 ;zap QBIN
- .iff
- mov #'&,r0 ;we want to use &
- .endc
- 40$: movb r0,(r4)+
- ; CHKT=1 or what they say if they went first
- movb (r5)+,r0 ;get it
- cmp r0,r1 ;default (=1)?
- beq 50$ ;yes
- sub #'1,r0 ;find value (0,1,2)
- cmp r0,#2 ;valid?
- blos 60$ ;yes
- 50$: clr r0 ;no
- 60$: inc r0 ;+1 (1,2,3)
- movb mchkt,r2 ;have we already voted?
- bne 70$ ;yes
- ; they're going first, so their vote wins
- movb r0,(r4)+ ;save
- add #'0,r0 ;convert back
- movb r0,mchkt ;we'll agree
- br 90$
- 70$: ; we already decided, if they agree that's it, otherwise 1
- sub #'0,r2 ;convert
- cmp r0,r2 ;do they agree?
- beq 80$ ;yes
- movb #'1,mchkt ;no, we'll use 1
- mov #1,r0
- 80$: movb r0,(r4)+
- 90$: ; REPT=none
- movb (r5)+,r0 ;get their char
- movb r0,mrept ;I'll agree if I haven't already
- cmp r0,r1 ;will we do it?
- bne .+4 ;yes
- clr r0 ;no
- movb r0,(r4)+
- ; CAPAS=none
- movb (r5)+,r0 ;get theirs
- sub r1,r0 ;UNCHAR()
- movb r0,(r4)+ ;save bits
- rts pc
- ;+
- ;
- ; Send error packet.
- ;
- ; r0 ptr to .asciz msg
- ;
- ;-
- err: incb seq ;seq +1
- bicb #^C77,seq ;isolate low 6
- mov r0,r5 ;copy
- mov r0,r4 ;twice
- 10$: tstb (r4)+ ;count
- bne 10$
- dec r4 ;-1
- sub r5,r4 ;length
- call ldatn ;load packet
- call ldatf ;fix for MAKPAC
- mov #'E,r0 ;type=ERROR
- call makpac ;make packet
- jmp putpac ;send it, return
- ;+
- ;
- ; Send an ACK for the current packet.
- ;
- ;-
- ack: clr r4 ;no data
- mov #txbuf+3,r5 ;space for header stuff
- ack1: ; enter with data field at (r5), length in r4
- mov #'Y,r0 ;type=ACK
- call makpac ;make a packet
- mov r5,ackdat ;save data
- mov r4,acklen
- incb seq ;bump seq
- bicb #^C77,seq ;mod 100
- jmp putpac ;send it, return
- ;+
- ;
- ; Resend ACK for previous packet.
- ;
- ;-
- reack: mov ackdat,r5 ;get ptr
- mov acklen,r4 ;and length
- jmp putpac ;send it
- ;+
- ;
- ; Send a NAK for the current packet.
- ;
- ;-
- nak: mov #'N,r0 ;NAK
- clr r4 ;no data
- mov #txbuf+3,r5 ;space for header stuff
- call makpac ;make a packet
- jmp putpac ;send it, return
- ;+
- ;
- ; Load data field.
- ;
- ; r5 data to load
- ; r4 length of data
- ; r2 length of buffer
- ; r1 buffer addr
- ;
- ; Each code is as follows:
- ; .byte '~,count+40 ;repeat count if rept.ne.0
- ; .byte '& ;8th-bit-quote if b7=1 and qbin.ne.0
- ; .byte '# ;ctrl-char-quote if needed ('# is my choice)
- ; .byte char ;char, with quoted bits trimmed
- ;
- ; Returns C=1 if output buf is full, in which case it's possible
- ; that not all of the data were transferred (r5, r4 updated).
- ;
- ; The LDATN entry sets up r1 and r2 to start a new packet.
- ; The LDATF entry converts r1, r2 returned from LDAT into r4, r5
- ; needed by MAKPAC, assuming we were using TXBUF as the buffer.
- ;
- ;-
- ldatn: ; set up for new packet
- mov #txbuf+3,r1 ;usual initial values for r1, r2
- movb maxl,r2
- ;br ldat
- ;
- ldat: tst r4 ;nothing to do?
- beq 170$ ;C=0 from TST
- br 150$ ;jump into loop
- 10$: ; dry run to see if this char will fit in the packet
- ; (we worry about this only when we're within 5 chars of full)
- movb (r5),r0 ;get next char
- ; 1 char for the char itself
- mov #1,r3 ;length so far
- ; 2 chars for repeat prefix
- tstb rept ;do we do compression?
- beq 20$
- cmp r4,#3 ;at least 3 chars left?
- blo 20$
- cmpb r0,1(r5) ;next one the same?
- bne 20$
- cmpb r0,2(r5) ;what about the one after?
- bne 20$
- add #2,r3 ;yep, compression takes 2 chars
- 20$: ; 1 char for 8th bit quote
- tstb qbin ;do we quote 8th bit?
- beq 30$
- tstb r0 ;8th bit set?
- bpl 30$
- inc r3 ;yes, add 1 char
- 30$: ; 1 char for ctrl quote or flag quote
- bic #^C177,r0 ;trim to 7
- cmp r0,#177 ;ctrl char?
- beq 40$
- cmp r0,#40
- blo 40$
- cmpb r0,#'# ;flag?
- beq 40$
- cmpb r0,qbin
- beq 40$
- cmpb r0,rept
- bne 50$
- 40$: inc r3 ;add 1 char
- 50$: cmp r2,r3 ;enough space?
- blo 170$ ;no, return C=1
- 60$: ; we're sure we have enough space, really do it
- movb (r5)+,r0 ;get the char
- tstb rept ;try to compress?
- beq 90$
- cmp r4,#3 ;.GE.3 chars?
- blo 90$
- cmpb r0,(r5) ;.GE.3 in a row the same?
- bne 90$
- cmpb r0,1(r5)
- bne 90$
- ; at least 3 in a row, do a repeat count
- add #2,r5 ;skip the next 2
- sub #2,r4 ;eat them
- mov #3,r3 ;init count
- 70$: cmp r4,#1 ;anything left? (r4 is still +1 here)
- beq 80$ ;no
- cmpb r0,(r5) ;yes, is it the same?
- bne 80$
- inc r5 ;yes, eat it
- dec r4 ;count it
- inc r3 ;rept count +1
- cmp r3,#94. ;field full?
- blo 70$ ;no, loop
- 80$: movb rept,(r1)+ ;save flag
- add #40,r3 ;char(count)
- movb r3,(r1)+
- sub #2,r2 ;count
- 90$: ; quote 8th bit
- tstb qbin ;binary quoting?
- beq 100$
- tstb r0 ;does it need it?
- bpl 100$
- movb qbin,(r1)+ ;yes
- dec r2
- bic #^C177,r0 ;isolate low 7
- 100$: ; quote control chars
- mov r0,r3 ;copy
- bic #^C177,r3 ;trim
- cmpb r3,#177 ;DEL?
- beq 110$
- cmpb r3,#40 ;ctrl char?
- bhis 120$
- 110$: mov #100,r3 ;get 100
- xor r3,r0 ;ctl(r0)
- br 130$ ;go quote
- 120$: ; see if it's a flag char
- ; we got #@ above so r3 can't be nul - OK to cmpb to QBIN & REPT
- cmpb r3,#'# ;qctl?
- beq 130$
- cmpb r3,qbin ;qbin?
- beq 130$
- cmpb r3,rept ;rept?
- bne 140$
- 130$: movb #'#,(r1)+ ;qctl
- dec r2
- 140$: ; write the char itself
- movb r0,(r1)+ ;write it
- dec r2
- dec r4 ;dec count
- beq 160$ ;done, skip
- 150$: cmp r2,#5 ;could we overrun?
- bhis 60$ ;no, don't worry
- br 10$ ;yes, be careful
- 160$: clc ;no flush needed yet
- 170$: rts pc ;(C set up)
- ;
- ldatf: ; convert r1, r2 from LDAT into r4, r5 for MAKPAC
- mov #txbuf+3,r5 ;point
- mov r1,r4 ;copy
- sub r5,r4 ;get length
- rts pc
- ;+
- ;
- ; Unpack the data field of a text packet.
- ;
- ; Handles all escapes, and as long as r2 and r3 are preserved parsing may be
- ; preserved around packet boundaries, which means that escape sequences may be
- ; broken between packets. After not mentioning whether this can happen in the
- ; first few versions, the 6th edition of the Kermit spec says it can't, so we
- ; won't generate them but we'll receive them OK.
- ;
- ; On entry:
- ; r2 escape bits: 200 if & encountered, 100 if # encountered
- ; r3 repeat count, or -1 if next char is char(repeat count)
- ; r4 length of input packet buffer
- ; r5 input packet buffer
- ;
- ; BUFPTR contains the current output buffer addr
- ; BUFCTR contains the # of free bytes in the buf at bufptr
- ;
- ; Call is through r1:
- ; jsr r1,unpack
- ; .word flush
- ; ... returns here, C=1 if flush error
- ;
- ; FLUSH is the addr of a routine which is called when BUFCTR reaches 0. It
- ; should start the old buf flushing and set up BUFPTR,BUFCTR to point to a
- ; fresh buffer for subsequent data. R0 may be destroyed by the routine, all
- ; others must be preserved. If the routine returns C=1, UNPACK returns
- ; immediately with C=1.
- ;
- ; The initial values for r2 and r3 are 0 and 1, respectively
- ; (no escapes yet and no repeat so we'll write 1 byte).
- ; Call IUNPK instead to set these up.
- ;
- ;-
- iunpk: ; come here to init flags
- clr r2 ;no escapes
- mov #1,r3 ;repeat count = 1
- unpack: ; come here with flags already initted
- tst r4 ;anything to unpack?
- beq 60$ ;no
- 10$: movb (r5)+,r0 ;get next char
- tst r3 ;expecting repeat count?
- bmi 90$ ;yes
- cmpb r0,rept ;repeat flag?
- beq 80$
- cmpb r0,qbin ;8th-bit flag (if any)?
- beq 100$
- cmpb r0,qctl ;ctrl flag?
- beq 110$
- 20$: xor r0,r2 ;we've finished the char, flip bits
- 30$: ; save r2, r3 times
- movb r2,@bufptr ;put in buf
- inc bufptr ;bump ptr
- dec bufctr ;any space left?
- beq 70$ ;no, queue write
- 40$: sob r3,30$ ;loop
- clr r2 ;re-init flags
- inc r3 ;count=1
- 50$: sob r4,10$ ;loop
- 60$: tst (r1)+ ;skip flush addr, C=0
- rts r1
- 70$: ; go flush buffer
- call @(r1) ;flush
- bcc 40$ ;loop if ok
- tst (r1)+ ;skip flush addr
- sec ;C=1
- rts r1
- 80$: ; repeat flag
- bit #100,r2 ;quoted?
- bne 120$ ;yes
- mov #-1,r3 ;no, next char is count
- br 50$ ;get it
- 90$: ; repeat count
- sub #40,r0 ;unchar
- mov r0,r3 ;save
- br 50$ ;get next
- 100$: ; 8th bit flag
- bit #100,r2 ;quoted?
- bne 120$ ;yes
- bis #200,r2 ;no, set 8th bit
- br 50$ ;C4
- 110$: ; ctrl flag
- bit #100,r2 ;quoted?
- bne 120$ ;yes
- bis #100,r2 ;no, set ctrl bit
- br 50$ ;C4
- 120$: bic #100,r2 ;clear flag (quoted, not ctrl)
- br 20$ ;save char
- ;+
- ;
- ; Send a simple packet and get an ACK for it.
- ;
- ; Enter with packet type in r0.
- ;
- ; Exit with things set up from SNDACK.
- ;
- ;-
- sndsmp: mov #txbuf+3,r5 ;ptr
- clr r4 ;no data
- call makpac ;make a packet
- ;br sndack ;send it, get ACK
- ;+
- ;
- ; Send a packet and get an ACK for it.
- ;
- ; Enter with r4, r5 set up for PUTPAC.
- ;
- ; Return with C=1 = retry count exhausted,
- ; C=0 = things are OK (getpac regs), seq updated.
- ;
- ;-
- sndack: mov #10.,-(sp) ;retry count
- 10$: call putpac ;send
- mov r4,-(sp) ;save
- mov r5,-(sp)
- call getpac ;get a packet
- bcc 30$ ;got one
- 20$: mov (sp)+,r5 ;restore
- mov (sp)+,r4
- dec (sp) ;give up yet?
- bne 10$ ;no
- tst (sp)+ ;yes, flush
- sec ;C=1
- rts pc
- 30$: cmp r0,#'Y ;ACK?
- bne 40$ ;no
- cmpb r1,seq ;correct sequence #?
- beq 50$ ;yes, skip
- 40$: cmp r0,#'N ;NAK?
- bne 20$ ;no, keep trying
- inc r1 ;seq+1
- bic #^C77,r1 ;mod 100'
- cmpb r1,seq ;NAK for next packet?
- bne 20$ ;no, keep trying
- clr r4 ;shouldn't be any data
- 50$: incb seq ;bump seq
- bicb #^C77,seq ;mod 100'
- add #6,sp ;purge stack, C=0
- rts pc
- ;+
- ;
- ; Make a packet.
- ;
- ; On entry:
- ;
- ; r0 packet type
- ; r4 length of dat
- ; r5 ptr to dat (must have 3 bytes free at each end)
- ;
- ; On return:
- ;
- ; r4 length of packet
- ; r5 ptr to packet
- ;
- ;-
- makpac: movb r0,-(r5) ;save typ
- movb seq,r0 ;get seq #
- add #40,r0 ;char(seq)
- movb r0,-(r5) ;save seq
- add #2,r4 ;count both
- mov r4,r0 ;copy
- add lchk,r0 ;add length of check
- mov r0,-(sp) ;save
- add #40,r0 ;take char(len)
- movb r0,-(r5) ;save len
- mov r5,r1 ;copy ptr
- inc r4 ;count length field
- add r4,r1 ;add length
- mov r5,-(sp) ;save
- call @checka ;compute check
- mov (sp)+,r5 ;restore
- mov (sp)+,r4
- inc r4 ;count length
- rts pc
- ;+
- ;
- ; Send a packet.
- ;
- ; On entry,
- ;
- ; r4 length of len through chk fields
- ; r5 ptr to len field
- ;
- ; Preserves r4 and r5.
- ;
- ;-
- putpac: .rctrlo ;might have received ^O in line noise
- movb npad,r1 ;get # pads to send
- beq 20$ ;none
- movb padc,r0 ;get char
- 10$: .ttyout ;write one
- sob r1,10$ ;loop
- 20$: .ttyout #soh ;write SOH
- mov r4,r2 ;copy
- mov r5,r3
- 30$: movb (r3)+,r0 ;get next char
- .ttyout ;write it
- sob r2,30$ ;loop
- movb eol,r0 ;write eol char
- .ttyout
- rts pc
- ;+
- ;
- ; Receive a packet.
- ;
- ; On return:
- ;
- ; If successful, C=0 and
- ;
- ; r0 packet type
- ; r1 packet sequence number
- ; r4 length of data field
- ; r5 ptr to data field
- ;
- ; C=1 on timeout, bad checksum, or obviously invalid length.
- ;
- ;-
- getpac: jsr r5,gtmout ;get mark
- .word 50$ ;whoops
- cmp r0,#soh ;is this mark?
- bne getpac ;loop if not
- mov #rxbuf,r5 ;pt at buf
- mov r5,r4 ;copy
- jsr r5,gtmout ;get length
- .word 50$ ;whoops
- movb r0,(r4)+ ;save
- sub #40,r0 ;unchar(len)
- cmp r0,#136 ;valid?
- bhi 50$ ;nope, don't rape core
- mov lchk,r1 ;get length of check
- add #2,r1 ;+2 (seq, typ)
- cmp r0,r1 ;too small for null data field?
- blo 50$ ;yes, forget it
- mov r0,r1 ;copy length
- 10$: jsr r5,gtmout ;get a char
- .word 50$ ;whoops
- movb r0,(r4)+ ;save the char
- sob r1,10$ ;loop
- ; got the whole thing, check it
- sub r5,r4 ;find length
- sub lchk,r4 ;don't check the check
- mov r4,-(sp) ;save
- mov r5,-(sp)
- mov #chkbuf,r1 ;pt at buf
- call @checka ;check the packet
- mov r5,r2 ;copy check ptr
- mov (sp)+,r5 ;restore
- mov (sp)+,r4
- sub r3,r1 ;back up
- 20$: cmpb (r1)+,(r2)+ ;right?
- bne 40$ ;no
- sob r3,20$ ;loop
- 30$: inc r5 ;skip LEN
- movb (r5)+,r1 ;get seq
- sub #40,r1 ;unchar
- bcs 50$ ;whoops
- bit #^C77,r1 ;must fit in 6 bits
- bne 50$ ;doesn't, error
- movb (r5)+,r0 ;get type
- sub #3,r4 ;update length (C=0)
- rts pc
- 40$: ; bad check -- if check type .NE. 1-char-checksum, see if the packet
- ; would have been valid if it were; this way we can recover from them
- ; losing our half of parms negotiation
- ;;; we may want to limit this check to cases when it could happen, otherwise
- ;;; we'll blindly accept bad packets 1/256th of the time
- mov lchk,r0 ;get check type
- cmp r0,#1 ;=1?
- beq 50$ ;yes, no point in being cute
- add r0,r4 ;fix length
- dec r4 ;to include everything but 1-char-checksum
- mov r4,-(sp) ;save
- mov r5,-(sp)
- mov #chkbuf,r1 ;pt at buf
- call chk1 ;call 1-char-checksum routine
- mov r5,r2 ;save
- mov (sp)+,r5 ;restore
- mov (sp)+,r4
- cmpb (r1),(r2) ;match?
- beq 30$ ;yes, continue processing
- 50$: sec ;error return
- rts pc
- ;
- gtmout: ; get char with timeout
- .ttinr ;try to get a char
- bcs gtmout ;loop
- tst (r5)+ ;skip return
- rts r5
- ;+
- ;
- ; Check routines.
- ;
- ; On entry:
- ;
- ; r1 buffer to put check in
- ; r4 length of len through dat fields
- ; r5 len field of packet to check
- ;
- ; On return:
- ;
- ; r1 updated
- ; r3 length of check generated
- ; r5 end of region checked
- ;
- ;-
- chk1: ; 1-byte checksum
- ; chk = <<sum+<<sum/100>&3>>&77>+40 (sum is 8-bit sum of chars)
- clr r2 ;init sum
- 10$: movb (r5)+,r0 ;get a char
- add r0,r2 ;add it in
- sob r4,10$ ;loop
- mov r2,r0 ;copy
- rolb r2 ;left 3
- rolb r2
- rolb r2
- bic #^C3,r2 ;isolate <7:6>
- add r2,r0 ;find total
- bic #^C77,r0 ;isolate <5:0>
- add #40,r0 ;char(chk)
- movb r0,(r1)+ ;save
- mov #1,r3 ;length
- rts pc
- ;
- chk2: ; 2-byte checksum
- ; chk1 = sum&77+40, chk2 = <sum_-6>&77+40 (sum is 12-bit sum of chars)
- clr r2 ;init sum
- 10$: clr r0 ;clear high
- bisb (r5)+,r0 ;get a char
- add r0,r2 ;add it in
- sob r4,10$ ;loop
- mov r0,r2 ;copy
- bic #^C77,r0 ;low 6 bits
- add #40,r0 ;char()
- movb r0,(r1)+ ;save
- asl r2 ;left 2
- asl r2
- swab r2 ;and right 8 = right 6
- bic #^C77,r2 ;high 6 bits
- add #40,r2 ;char()
- movb r2,(r1)+ ;save
- mov #2,r3 ;length
- rts pc
- ;
- chk3: ; 3-byte CRC (requires EIS)
- ; algorithm stolen from MS-Kermit V2.24
- ; (written by Columbia University)
- clr r2 ;init
- 10$: movb (r5)+,r0 ;get next
- .if ne eis$$
- xor r2,r0 ;XOR low byte of old value
- .iff
- mov r2,r3 ;save
- bis r2,r0 ;find IOR
- com r2 ;find AND
- bic r2,r3
- bic r3,r0 ;(r2!r0)&^C(r2&r0)
- .endc
- bic #^C377,r0 ;isolate
- asl r0 ;*2
- mov crc(r0),r0 ;get bits
- clrb r2 ;running total right 8.
- swab r2
- .if ne eis$$
- xor r0,r2 ;find new value
- .iff
- mov r0,r3 ;save
- bis r0,r2 ;IOR
- com r0 ;AND
- bic r0,r3
- bic r3,r2 ;XOR
- .endc
- sob r4,10$ ;yay
- .if ne eis$$
- mov r2,r3 ;copy
- ash #-6,r2 ;right 6
- mov r2,r0
- ash #-6,r0 ;again
- .iff
- mov r2,r3 ;copy
- mov r2,r0
- swab r0 ;right 12. (right 8., right 4)
- asr r0
- asr r0
- asr r0
- asr r0
- asl r2 ;right 6. (left 2, right 8.)
- asl r2
- swab r2
- .endc
- bic #^C17,r0 ;<15:12>
- bis #40,r0 ;char()
- movb r0,(r1)+
- bic #^C77,r2 ;<11:6>
- add #40,r2 ;char()
- movb r2,(r1)+
- bic #^C77,r3 ;<5:0>
- add #40,r3 ;char()
- movb r3,(r1)+
- mov #3,r3 ;length
- rts pc
- ;
- crc: .word 1,2,3,4 ;this table will be 256. words
- ;
- .rem %
- xor dx,dx ;init crc
- mov bh,dl ;bh=0
- kchk3a: lodsb ;get next byte
- xor al,dl ;XOR in old value
- mov dl,dh ;right 8 bits
- mov dh,al ;save low byte
- mov bl,al ;copy
- and bl,17 ;isolate low 4
- shl bl,1 ;*2
- mov ax,ds:crc1[bx] ;get low part
- mov bl,dh ;copy again
- shr bl,1 ;right-justify high nibble, *2
- shr bl,1
- shr bl,1
- and bl,36 ;isolate
- xor ax,ds:crc2[bx] ;bitwise add get high part
- mov dh,ah ;copy high half
- xor dl,al ;bitwise add low half
- loop kchk3a ;loop
- mov bx,dx ;copy
- mov cl,6 ;bit count
- shr bx,cl ;right 6
- mov ax,bx ;one more time
- shr ax,cl ;right 6
- or al,40 ;take char(CRC<15:12>)
- mov [di],al ;save it
- inc di ;+1
- and bl,77 ;isolate CRC<6:11>
- add bl,40 ;take char()
- mov [di],bl ;save
- inc di ;+1
- and dl,77 ;isolate CRC<5:0>
- add dl,40 ;take char()
- mov [di],dl ;save
- inc di ;+1
- mov cl,3 ;byte count=3
- ret
- %
- .sbttl pure data
- ;
- rad50a: ; 1st char rad50 lookup table
- .rad50 " A B C D E F G "
- .rad50 "H I J K L M N O "
- .rad50 "P Q R S T U V W "
- .rad50 "X Y Z $ . 0 1 "
- .rad50 "2 3 4 5 6 7 8 9 "
- ;
- rad50b: ; 2nd char rad50 lookup table
- .rad50 " A B C D E F G "
- .rad50 " H I J K L M N O "
- .rad50 " P Q R S T U V W "
- .rad50 " X Y Z $ . 0 1 "
- .rad50 " 2 3 4 5 6 7 8 9 "
- ;
- checks: .word chk1,chk2,chk3
- ;
- larea: .byte 1,1 ;.LOOKUP, channel = 1
- .word fbuf ;filename
- .word -1 ;start at head posn on magtape
- ;
- ludir: .byte 0,1 ;.LOOKUP, channel = 0
- .word wlddev ;ptr to device name
- .word 0 ;(only for MT:)
- ;
- r50t: .ascii " ABCDEFGHIJKLMNOPQRSTUVWXYZ$.%0123456789"
- r50tnb: .ascii <0>"ABCDEFGHIJKLMNOPQRSTUVWXYZ$.%0123456789"
- ;
- months: .ascii "-Jan-Feb-Mar-Apr-May-Jun-Jul-Aug-Sep-Oct-Nov-Dec-"
- ;
- ; strings for dirsum
- tfile: .asciz " file"
- tblk: .asciz " block"
- .asciz " in use" ;must follow tblk
- tfree: .asciz " free"
- ;
- bfs: .asciz 'Bad file specification.'
- bdn: .asciz 'Bad device name.'
- nomtch: .asciz 'No matching files found.'
- fnf: .asciz 'File not found.'
- ucf: .asciz 'Unable to create file.'
- uof: .asciz 'Unable to open file.'
- werr: .asciz 'Error writing file.'
- rerr: .asciz 'Error reading file.'
- ioerr: .asciz 'I/O error.'
- toolng: .asciz 'Line too long.'
- mkw: .asciz 'Missing keyword.'
- ;
- .sbttl some of both
- ;
- .even
- ;
- earea: .byte 1,2 ;.ENTER, channel = 1
- .word fbuf ;dblk
- elen: .word -1 ;length=max (or value if we know)
- .word -1 ;add file at EOT if magtape
- ;
- device: .word 0 ;currently loaded device handler, or 0 if none
- lchk: .word 1 ;length of checksum (bytes)
- checka: .word chk1 ;addr of routine to compute checksum
- ;
- warea: .byte 1,11 ;.WRITE, channel = 1
- wblk: .word ;blk #
- wca: .word ;core address
- wwc: .word ;word count
- .word 1 ;no crtn
- ;
- rarea: .byte 1,10 ;.READ, channel = 1
- rblk: .word ;blk #
- rca: .word ;core address
- rwc: .word ;word count
- .word 1 ;no crtn
- ;
- rddir: .byte 0,10 ;.READ, channel = 0
- dirblk: .word ;blk #
- .word buf1 ;core addr
- .word 1000 ;word cnt (dir segments are 2 blks)
- .word 0 ;wait for completion
- ;
- wrdir: .byte 0,11 ;.WRITE, channel=0
- wdrblk: .word ;blk #
- .word buf1 ;core addr
- .word 1000 ;word cnt (2 blocks)
- .word 0 ;wait for completion
- ;
- wlddev: .word ;dev name for dir search
- .word 0,0,0 ;no filename or ext
- ;
- .blkb 3 ;for len, seq, typ
- mparms: ; my parameters
- .byte 94.+40 ;MAXL (anything's OK with us)
- .byte 5+40 ;TIME (line speed should be only problem)
- .byte 0+40 ;NPAD (no pad chars)
- .byte '@ ;PADC (doesn't matter)
- .byte cr+40 ;EOL (doesn't matter)
- .byte '# ;QCTL (hard-coded - doesn't really matter)
- mqbin: .byte ;QBIN (only if one of us needs it)
- mchkt: .byte ;CHKT (whatever they want, or 1 byte)
- mrept: .byte ;REPT (repeat char)
- .byte attr+40 ;CAPAS (attr packets OK)
- nmprms= .-mparms
- .blkb 3 ;for check
- ;
- pns: .ascii 'Packet type "'
- pnsc: .byte
- .asciz '" not supported.'
- ;
- cns: .ascii 'Generic command "'
- cnsc: .byte
- .asciz '" not supported.'
- ;
- pvl: .ascii 'Packet type "'
- pvlc: .byte
- .asciz '" invalid at this point.'
- ;
- ddev: .ascii 'Default device is now '
- ddev1: .blkb 3+1+2 ;<ddu>:<crlf>
- ;
- .sbttl pure storage
- ;
- .even
- defdev: .blkw ;default device name (.rad50)
- fbuf: .blkw 4 ;device, filename, extension
- dstat: .blkw 4 ;.DSTAT area
- ackdat: .blkw ;ptr to last ACK packet
- acklen: .blkw ;length of last ACK packet
- ; directory stuff:
- extbyt: .blkw ;extra bytes per dir entry
- files: .blkw ;no. of files in dir listing
- used: .blkw ;total no. blks in use
- free: .blkw ;total no. < UNUSED > blks
- segnxt: .blkw ;next segment in dir
- ;
- seq: .blkb 1 ;packet sequence #
- txbuf: .blkb 3+91.+3 ;tx packet buffer
- rxbuf: .blkb 91.+3 ;rx packet buffer
- chkbuf: .blkb 3 ;check buffer (for generated rx check)
- wild: .blkb 91.+1 ;wildcard buffer for GD and R
- wldflg: .blkb ;NZ => WILD contains at least 1 wildcard char
- dirall: .blkb ;NZ => show all dir entries (no wildcard check)
- dirnon: .blkb ;NZ => don't build dir entry table (usage check)
- binfil: .blkb ;NZ => don't trim NULs from ends of file blks
- ;
- maxl: .blkb ;maximum packet length (bytes)
- time: .blkb ;packet timeout (seconds)
- npad: .blkb ;no. of pad characters
- padc: .blkb ;pad character (if npad.ne.0)
- eol: .blkb ;eol char
- qctl: .blkb ;ctrl char quote
- qbin: .blkb ;8th bit quote
- chkt: .blkb ;check type
- rept: .blkb ;repeat char
- capas: .blkb ;extra capabilities
- nparms= .-maxl
- ;
- .even
- cbuf: .blkw ;current buffer in double-buffering
- rlen: .blkw ;number of words reading into next buffer
- bufptr: .blkw ;ptr into buffer
- bufctr: .blkw ;ctr in buffer
- ;
- matlst: .blkw 72.*5+1 ;wildcard match list, up to 72. entries + zero
- ;
- buf1: .blkb bufsiz ;buffers
- buf2: .blkb bufsiz
- ;
- devhnd= . ;device handlers go here
- .end start
-