!
! Name: MRECV
!
! Function: This is the receive module for the MacTerminal protocol
! support on the AlphaMicro. MTRANS provides the transmit support.
! This protocol is similar to and uses the same as XMODEM protocol.
! First, the sender sends an escape sequence to tell the receiver to go
! into receive mode. Then it sends the file information block in standard
! XMODEM format, then an EOT. Then XMODEM protocol is used twice, all
! the way from the beginning NAK from the receiver to the ending
! EOT from the sender and ACK from receiver; the first time the data
! fork is sent; then the resource fork.
! This version also adds four lines to a temporary directory file.
! The first line is the filename; the second is the initials of the
! uploader; the third and fourth are two lines of description.
!
! Author: Tom Dahlquist
!
! Edit History:
! When Who What
! 11/08/84 TAD Written (from XRECV).
! 01/16/84 TAD Two fixes to improve reliability. 1) Ignore EOT if
! block count isn't right; 2) ignore CTL-C unless
! he hits three of them in a row.
! 01/24/85 TAD Use DEL or CTL-C to kill it.
! 05/09/85 TAD Use CMPCRC to compute CRC.
!
! The following is a map of one block as transmitted by XMODEM or
! Macterminal. First is an SOH, then the block number, then the
! inverted block number, then 128 data bytes, then the checksum.
!
MAP1 IO'BLOCK
MAP2 RCV'BLOCK'COUNT,B,1
MAP2 NOT'BLOCK'COUNT,B,1
MAP2 IO'REC,X,128
MAP2 RCV'CHECK'SUM,B,1
!
! This is the map of the file information block sent by MacTerminal.
! The filler byte sent after the SOH, block #, inverted block # has
! been kept as the first byte of the block for simplicity, since the
! AlphaMicro doesn't really care about alignment.
!
MAP1 FILE'INFO,@IO'REC
MAP2 FILL1,X,1 ! first filler byte
MAP2 FNAME,X,64 ! MacIntosh file name
MAP2 FTYPE,S,4 ! file type
MAP2 FCREATE,S,4 ! file creator
MAP2 FFLAGS,B,2 ! file flags
MAP2 FLOC,B,4 ! location in folder
MAP2 FFOLDER,B,2 ! file folder
MAP2 LOCKED,B,2 ! locked flags
MAP2 DSIZE,X,4 ! size of data fork
MAP2 RSIZE,X,4 ! size of resource fork
MAP2 FLCRDAT,B,4 ! creation date
MAP2 FLMDDAT,B,4 ! modification date
MAP2 FILL2,X,29 ! filler
MAP1 IN'CHR,X,1
MAP1 TEST'CHR,X,1
MAP1 OPTION,F
MAP1 LENGTH,F
MAP1 OUT'CHR,X,1
MAP1 DISK'FSPEC,S,24
MAP1 IN'UNIT,F,,0
MAP1 OUT'UNIT,F,,0
MAP1 DISK'UNIT,F,,3
MAP1 ERROR'UNIT,F,,4
! MAP1 COM'FSPEC,S,24
MAP1 SOH,X,1,CHR$(1)
MAP1 CTRLC,X,1,CHR$(127)
MAP1 DEL,X,1,CHR$(255)
MAP1 EOT,X,1,CHR$(4)
MAP1 ACK,X,1,CHR$(6)
MAP1 NAK,X,1,CHR$(21)
MAP1 ESCAPE,X,1,CHR$(27)
MAP1 CHECK'SUM,B,1
MAP1 BLOCK'LENGTH,F,,128
MAP1 BLOCK'COUNT,F,,1 ! must be B,1 to wrap around correctly
MAP1 TEMP'COUNT,B,1
MAP1 NAK'COUNT,F
MAP1 I,F
MAP1 J,F
MAP1 TEN'SECS,F,,10
MAP1 EOT'FOUND,F
MAP1 DATA'ERROR,F
MAP1 PREV'BLOCK,F
MAP1 DEBUG,F
MAP1 TRUE,F,,-1
MAP1 FALSE,F,,0
MAP1 MODE,B,2,1
MAP1 LOCK1,B,2,59999
MAP1 LOCK2,B,2,0
MAP1 DIR'FILE,S,25,"SYSOP.NEW"
MAP1 LINES(6),S,80
MAP1 INITIALS,S,4
MAP1 NUM'BLOCKS,F
MAP1 TEMP1,X,4
MAP1 TEMP1X(4),X,1,@TEMP1
MAP1 TEMP2,X,4
MAP1 TEMP2X(4),X,1,@TEMP2
MAP1 TEMP'SIZE,B,4,@TEMP2
MAP1 SIZED,F
MAP1 SIZER,F
MAP1 SIZE,F
!
! Get the filename. Try to ensure that it is a valid AMOS filename
! and that nothing by that name already exists.
!
GET'FILENAME:
INPUT LINE "Filename to save as (six letters or less): ",DISK'FSPEC
IF UCS(DISK'FSPEC[-2,-1])="/D" THEN &
DISK'FSPEC=DISK'FSPEC[1,-3] : &
OPEN #99,"MRECV.LST",OUTPUT : &
?#99,"MRECV ERROR LISTING" : &
DEBUG=TRUE
DISK'FSPEC=UCS(DISK'FSPEC)
I=INSTR(1,DISK'FSPEC,".")
J=LEN(DISK'FSPEC)
IF I=0 GOTO NO'PERIOD
IF I>7 GOTO TOO'LONG
IF J-I>3 GOTO BAD'EXT
GOTO CHECK'FILENAME
NO'PERIOD:
IF J>6 GOTO TOO'LONG
DISK'FSPEC=DISK'FSPEC+".MAC"
CHECK'FILENAME:
LOOKUP DISK'FSPEC,I
IF I=0 GOTO FILENAME'OK
?"Sorry, that filename already exists!"
GOTO GET'FILENAME
TOO'LONG:
?"Didn't I say six letters or less?"
GOTO GET'FILENAME
BAD'EXT:
?"Sorry, filename extension can only be three letters in length!"
GOTO GET'FILENAME
FILENAME'OK:
?"Saving file as ";DISK'FSPEC;"..."
!
! Get the two lines of description. Try to make sure that he at
! least enters something.
!
?"Please enter up to six lines of description:"
FOR I=1 TO 6
GET'LINE:
INPUT LINE ">",LINES(I)
IF LINES(I)="" THEN &
IF I=1 THEN &
?"Come on, you must know SOMETHING about this file!" : &
GOTO GET'LINE &
ELSE &
I=6
NEXT
!
! Now, add our four lines of data to the description file. Use
! XLOCK to make sure no one else is writing to it.
!
?"One moment please...writing description to directory."
XCALL XLOCK,MODE,LOCK1,LOCK2
LOOKUP DIR'FILE,I
IF I=0 THEN &
OPEN #98,DIR'FILE,OUTPUT &
ELSE &
OPEN #98,DIR'FILE,APPEND
?#98,DISK'FSPEC
XCALL XMEM,17,INITIALS
?#98,INITIALS
FOR I=1 TO 6
IF LINES(I)#"" THEN &
?#98,LINES(I) &
ELSE &
I=6
NEXT
CLOSE #98
MODE=2
XCALL XLOCK,MODE,LOCK1,LOCK2
!
! OK, let's go. Put us into data mode, flush our input buffer,
! and wait for the ESCape, "a" that the sender sends to begin the
! transmission. If we don't get the ESCape in 60 seconds, we
! timeout.
!
?"OK, please go to the FILE menu and use SEND to begin the transmission."
XCALL IMG
OPEN #DISK'UNIT, DISK'FSPEC, OUTPUT
! INPUT LINE "Communications trmdef?",COM'FSPEC
! COM'FSPEC = ""
! OPEN #IN'UNIT, "TRM:"+COM'FSPEC, INPUT
! OPEN #OUT'UNIT, "TRM:"+COM'FSPEC, OUTPUT
OPTION=0
FLUSH'BUFFER:
CALL GET'ONE
IF LENGTH#0 GOTO FLUSH'BUFFER
OPTION=60
CALL GET'ONE
IF LENGTH=0 GOTO TIMEOUT1
IF IN'CHR=ESCAPE GOTO OK1
IF DEBUG THEN &
?#99,"FIRST CHARACTER RECEIVED NOT ESCAPE"
GOTO DIE
OK1:
OPTION=10
CALL GET'ONE
IF LENGTH=0 GOTO TIMEOUT1
IF IN'CHR="a" GOTO OK2
IF DEBUG THEN &
?#99,"SECOND CHARACTER RECEIVED NOT a"
GOTO DIE
OK2:
OUT'CHR=ACK
CALL SEND'ONE
OPTION=10
CALL GET'ONE
IF LENGTH=0 GOTO TIMEOUT2
CALL GET'BLOCK ! get header block...
IF NOT EOT'FOUND GOTO OK3
IF DEBUG THEN &
?#99,"EOT FOUND WHILE WAITING FOR FIB"
GOTO DIE
OK3:
IF NOT DATA'ERROR GOTO OK4
IF DEBUG THEN &
?#99,"DATA ERROR ON FIRST BLOCK"
GOTO DIE
OK4:
OPTION=10
CALL GET'ONE ! must be EOT...
IF LENGTH=0 GOTO TIMEOUT2
IF IN'CHR=EOT GOTO OK5 ! currently, no retransmission
! of file information block...
IF DEBUG THEN &
?#99,"EOT NOT FOUND AFTER FIRST BLOCK"
GOTO DIE
OK5:
OUT'CHR=ACK
CALL SEND'ONE
TEMP1=DSIZE
CALL MASSAGE
SIZED=TEMP'SIZE
TEMP1=RSIZE
CALL MASSAGE
SIZER=TEMP'SIZE
SIZE=SIZED
CALL SEGMENT
SIZE=SIZER
CALL SEGMENT
END ! presumably successful completion.
!
! Receive one fork of the file. This amounts to a complete XMODEM
! transmission, starting with the receiver (us) sending a NAK, and
! ending with the sender sending an EOT which we acknowledge with
! an ACK. Note that a fork may be null, in which case all we
! receive is the EOT.
!
SEGMENT:
BLOCK'COUNT=1
NUM'BLOCKS=INT(SIZE/128)
IF NUM'BLOCKS*128#SIZE THEN NUM'BLOCKS=NUM'BLOCKS+1
CALL NAK'IT
SEG'LOOP:
CALL GET'BLOCK
IF DATA'ERROR THEN &
CALL NAK'IT : &
GOTO SEG'LOOP
IF NOT EOT'FOUND GOTO NOT'EOT
IF BLOCK'COUNT#NUM'BLOCKS+1 THEN &
CALL NAK'IT : &
GOTO SEG'LOOP
OUT'CHR=ACK
CALL SEND'ONE
RETURN
NOT'EOT:
OPTION=10
CALL GET'ONE
IF LENGTH=0 GOTO TIMEOUT2
GOTO SEG'LOOP
!
! Receive one data block. The sequence is SOH, block #, inverted
! block #, 128 data bytes, and one byte checksum. If any of these
! isn't what it should be, we set a data error flag. We should also
! timeout if a couple of seconds passes between bytes, but we currently
! don't. If the first character we receive is a CTL-C, we abort.
! If the first character is an EOT, we set a flag and return.
! Note that due to the way NAK'IT works, the first character has
! already been received and is in IN'CHR when this routine is
! called.
! I think that we should also be checking for one or two other
! control characters, but I don't know what they are.
!
GET'BLOCK:
DATA'ERROR=FALSE
EOT'FOUND=FALSE
PREV'BLOCK=FALSE
IF IN'CHR = DEL THEN &
CALL DEL'ABORT : &
RETURN
IF IN'CHR=CTRLC THEN &
CALL CTRLC'ABORT : &
RETURN
IF IN'CHR = EOT THEN &
EOT'FOUND=TRUE : &
RETURN
IF IN'CHR <> SOH GOTO SET'ERROR
XCALL TIMEIN,2,IO'BLOCK,LENGTH
IF LENGTH#131 GOTO SET'ERROR
TEMP'COUNT=BLOCK'COUNT ! take MOD 256...
IF RCV'BLOCK'COUNT = TEMP'COUNT GOTO COUNT'OK
TEMP'COUNT=BLOCK'COUNT-1
IF RCV'BLOCK'COUNT # TEMP'COUNT GOTO SET'ERROR
PREV'BLOCK=TRUE
COUNT'OK:
IF 255-NOT'BLOCK'COUNT <> RCV'BLOCK'COUNT GOTO SET'ERROR
IF DEBUG THEN &
?#99,"GOT DATA BLOCK"
! CHECK'SUM = 0
! FOR I = 1 TO BLOCK'LENGTH
! CHECK'SUM = CHECK'SUM+ASC(IO'REC[I;1])
! NEXT
XCALL CMPCRC,IO'REC,CHECK'SUM
IF RCV'CHECK'SUM <> CHECK'SUM GOTO SET'ERROR
IF NOT PREV'BLOCK THEN &
PRINT #DISK'UNIT, IO'REC; : &
BLOCK'COUNT = BLOCK'COUNT+1
OUT'CHR=ACK
CALL SEND'ONE
RETURN
SET'ERROR:
DATA'ERROR=TRUE
RETURN
!
! Send a NAK and wait for something to come. If it doesn't come
! within ten seconds, send another NAK. Check the input buffer and
! wait for two seconds after receiving anything before sending the
! first NAK, to allow the input stream to clear. If we send six
! NAK's with no reply, die.
!
NAK'IT:
XCALL TIMEIN,1,IN'CHR,LENGTH
IF LENGTH#0 GOTO NAK'IT
NAK'COUNT=0
NAK'LOOP:
OUT'CHR=NAK
CALL SEND'ONE
NAK'COUNT=NAK'COUNT+1
OPTION=10
CALL GET'ONE
IF LENGTH#0 RETURN
IF NAK'COUNT=7 GOTO TIMEOUT2
GOTO NAK'LOOP
!
! Send one character.
!
GET'ONE:
XCALL TIMEIN,OPTION,IN'CHR,LENGTH
IF DEBUG AND LENGTH#0 THEN &
?#99,ASC(IN'CHR)
RETURN
!
! Get one input character.
SEND'ONE:
?#OUT'UNIT,OUT'CHR;
IF DEBUG THEN &
?#99,"-";ASC(OUT'CHR)
RETURN
!
! Since first block can't be retransmitted, just die if any error.
!
DIE:
OUT'CHR=NAK
CALL SEND'ONE
CALL KILL'FILE
END
!
! If DEL or CTL-C received, just die.
!
DEL'ABORT:
TEST'CHR=DEL
GOTO TEST'ABORT
CTRLC'ABORT:
TEST'CHR=CTRLC
TEST'ABORT:
DATA'ERROR=TRUE
OPTION=2
FOR I=1 TO 2
CALL GET'ONE
IF LENGTH=0 OR IN'CHR#TEST'CHR RETURN
NEXT
IF DEBUG THEN &
?#99,"ENDING DUE TO OPERATOR"
CALL KILL'FILE
END
!
! Didn't receive the starting sequence.
!
TIMEOUT1:
?"Sorry, I haven't received what I'm expecting from your Macintosh."
?"Are you sure you have XModem and MacTerminal selected in the"
?"FILE TRANSFER SETTINGS menu?"
CALL KILL'FILE
END
!
! Miscellaneous timeouts.
!
TIMEOUT2:
?"I'm sorry, but your Macintosh has stopped sending me information."
?"If your Mac seems OK to you, please try again."
?"If this happens repeatedly, please notify the SYSOP."
CALL KILL'FILE
END
!
! Kill the output file.
!
KILL'FILE:
CLOSE #DISK'UNIT
KILL DISK'FSPEC
RETURN
!
! Make Mac length fields understandable to AM.
!
MASSAGE:
FOR I=1 TO 4
TEMP2X(5-I)=TEMP1X(I)
NEXT
RETURN