home *** CD-ROM | disk | FTP | other *** search
- \ Xmodem protocol file transfer.
- \ Commands:
- \ send filename \ Sends the file
- \ receive filename \ Receives the file
- \ The serial line parameters are established by "init-modem", which you
- \ may edit if you need to use different ones. The xmodem protocol requires
- \ 8 data bits, so changing that parameter won't work.
-
- only forth also definitions
- needs modem modem.fth
- needs get-ticks interval.fth
-
- only forth also modem also definitions
- decimal
-
- variable checksum
- variable #errors 4 constant max#errors variable #naks
- variable receive-sector#
- variable send-sector#
- variable expected-sector
- variable #control-z's
-
- create sector-buf 128 allot variable sector-ptr
-
- 0 constant nul 24 constant can 1 constant soh
- 4 constant eot 6 constant ack 21 constant nak
- variable timer-init variable timer
-
- : timeout: \ name ( seconds -- )
- create ,
- does> @ ( seconds ) ticks/second * timer-init !
- ;
- 3 timeout: short-timeout 6 timeout: long-timeout 60 timeout: initial-timeout
- short-timeout
-
- \ Interface to the serial line:
- \
- \ init-modem --
- \ Establishes the desired baud rate and # of bits on the serial line
- \ m-key? -- flag
- \ Flag is true if a character is available on the serial line
- \ m-key -- char
- \ Gets a character from the serial line
- \ m-emit char --
- \ Puts the character out on the serial line.
-
- : init-modem
- 8-bits 1-stop-bit no-parity 9600-baud no-flow-control set-line
- ;
-
- \ Interface to disk files:
- \
- \ open-receive-file -- \ Input Stream: filename
- \ Opens file for writing.
- \ write-sector addr --
- \ Writes the 128 byte record starting at addr to the file.
- \ close-file --
- \ Closes the currently-open file
- \ read-sector addr -- flag
- \ Reads the next 128 sector from the file to the buffer
- \ at addr. Returns true on failure (i.e. end-of-file)
- \ open-send-file -- Input Stream: name
- \ Opens the named file for reading.
- \ file-size -- l.size
- \ l.size is a 32-bit number which is the length of the
- \ current file in bytes.
-
- variable modem-file \ file descriptor for current file
- modem-file off
- : close-file ( -- )
- modem-file @ ?dup if close then
- modem-file off
- ;
- : abort-end ( -- ) \ abort and clean up
- ." aborting." cr close-file quit
- ;
- : get-filename \ filename ( -- str )
- bl word dup count upper pad 18 cmove pad
- ;
- : bigbuf ( -- )
- pad 8192 modem-file @ fsetbuffer
- ;
- : open-receive-file \ name ( -- )
- get-filename dup file-exists? ( name flag )
- if
- dup ". ." already exists. Clobber it? "
- key dup emit cr upc ascii Y <> if abort then ( name )
- then
- new-file ofd @ modem-file !
- bigbuf
- ;
- : open-send-file \ name ( -- )
- get-filename read open
- dup 0= abort" can't open file" modem-file !
- bigbuf
- ;
- : read-sector ( addr -- end-of-file? )
- dup 128 modem-file @ fgets ( addr count )
- tuck + ( count end-addr )
- \ Pad with control z's if necessary
- over 128 swap - control z fill ( count )
- 0=
- ;
- : write-sector ( addr -- ) \ write out the sector
- \ Dump out any control z's left over from last time
- #control-z's @ 0
- ?do control z modem-file @ fputc loop
-
- \ Count the control z's at the end of the buffer
- #control-z's off
- dup dup 127 + ( addr addr end-address )
- do
- i c@ control z <>
- if leave then
- 1 #control-z's +!
- -1 +loop ( addr )
- 128 #control-z's @ - modem-file @ fputs
- ;
- : file-size ( -- l.size )
- modem-file @ fsize
- ;
-
- \ End of disk file interface words
-
- \ It would be nice to use control C, but the BDOS snarfs it
- : ?interrupt ( -- ) \ aborts if user types control Z
- key? if key control z = if abort-end then then
- ;
- : receive-setup \ filename ( -- )
- init-modem
- open-receive-file
- decimal
- receive-sector# off 1 expected-sector ! #naks off #control-z's off
- ;
- variable last-char
- : timed-in ( -- char | -1 ) \ get a character unless timeout
- get-ticks timer-init @ + timer !
- begin m-key? if m-key exit then
- timer @ reached?
- until -1
- ;
- : gobble ( -- ) \ eat characters until they stop coming
- short-timeout
- begin timed-in -1 = until
- long-timeout
- ;
- : receive-error ( -- ) \ eat rest of packet and send a nak
- gobble
- 1 #naks +! #naks @ max#errors >
- if can m-emit ." too many errors." cr
- abort-end
- then
- ." sent nak." cr nak m-emit
- ;
- : normal-end ( -- ) ( clean up )
- ." reception completed" cr
- ack m-emit close-file
- ;
- : receive-header ( -- f ) \ true if header error
- timed-in dup -1 = if ." h1 " exit then
- dup receive-sector# !
- timed-in dup -1 = if ." h2 " exit then
- 255 xor <>
- ;
- : .bogus-char ( char -- )
- base @ >r hex cr dup 2 .r r> base !
- ." h(" emit ." ) unexpectedly seen."
- ;
- : receive-sector ( -- f ) \ true if runt sector
- 0 checksum !
- false
- sector-buf 128 bounds
- do timed-in dup -1 =
- if ( false -1 ) nip leave then ( false char )
- dup i c! checksum +!
- loop ( runt-sector? )
- ;
- : receive-checksum ( -- f ) \ true if checksum error
- timed-in dup -1 <> ( char true | -1 false )
- if checksum @ 255 and <> then
- ;
- : receive-packet ( -- f ) \ true if end of transfer
- false timed-in
- case
- soh of endof
- nul of 1- exit endof
- can of ." remote cancel" cr abort-end endof
- eot of 1- normal-end exit endof
- -1 of ." timeout" receive-error exit endof
- ( default) .bogus-char receive-error exit
- endcase
- receive-header if ." h" receive-error exit then
- receive-sector if ." s" receive-error exit then
- receive-checksum if ." c" receive-error exit then
- sector-buf write-sector
- ack m-emit
- (cr expected-sector @ .
- 1 expected-sector +!
- #naks off
- ;
- : receive \ filename ( -- )
- receive-setup
- gobble nak m-emit
- begin ?interrupt receive-packet until
- ;
- : wait-ack ( -- ) \ wait for ack or can
- 0 #errors !
- begin #errors @ max#errors > #naks @ max#errors > or
- if ." too many errors." cr can m-emit abort-end then
- ?interrupt
- timed-in
- case
- -1 of 1 #errors +! ." timeout" cr endof
- can of ." remote cancel" cr abort-end endof
- ack of #naks off exit endof
- nak of 1 #naks +! exit endof
- ( default) dup .bogus-char
- endcase
- again
- ;
- : wait-nak ( -- ) \ wait for nak
- initial-timeout timed-in
- case
- -1 of ." timeout" cr abort-end endof
- can of ." remote cancel" cr abort-end endof
- nak of 1 #naks +! exit endof
- ( default) dup .bogus-char
- endcase long-timeout
- ;
- : send-header ( -- ) \ header is soh sector# sector#not
- soh m-emit
- send-sector# @ 255 and dup m-emit
- 255 xor m-emit
- (cr send-sector# @ .
- ;
- : send-sector ( -- )
- 0 checksum !
- sector-buf 128 bounds
- do i c@ dup m-emit checksum +! loop
- ;
- : send-checksum ( -- )
- checksum @ 255 and m-emit
- ;
- : end-send ( -- )
- close-file
- begin eot m-emit wait-ack #naks @ 0= until
- ." file sent."
- ;
- : send-setup
- init-modem
- open-send-file
- decimal
- ." File size: " file-size dup ul.
- ." Estimated # sectors: " 127 + 128 / ul. cr
- 1 send-sector# !
- ;
- : send \ filename ( -- )
- send-setup
- gobble wait-nak #naks off
- begin ?interrupt
- #naks @ 0=
- if sector-buf read-sector if end-send exit then then
- send-header send-sector send-checksum
- wait-ack
- #naks @ 0= if 1 send-sector# +! then
- again
- ;
-