!
!       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