!
!       NAME:  MTRANS
!
!       FUNCTION:  This is the transmit module for the MacTerminal protocol
!       support on the AlphaMicro.  MRECV provides the receive 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.
!
!       AUTHOR:  Tom Dahlquist
!
!       EDIT HISTORY:
!         When   Who What
!       11/10/84 TAD Written (from XTRANS).
!       01/16/84 TAD Several changes.  1) Use FILEIN for input from file, thus
!                       allowing us to use RUN instead of XRUN.  2) Use TIMEIN
!                       for input from terminal, allowing timeouts.  3)  Ignore
!                       CTL-C unless three of them hit in a row.  4) Fix little
!                       bug in TRY'AGAIN.
!       01/24/85 TAD Accept either three DEL's or CTL-C's to kill us.
!       05/09/85 TAD Use CMPCRC to compute CRC.
!
!       This map is for the entire contents of one XMODEM transmission.
!       First is the SOH, then the block #, then the inverted block #,
!       then 128 data bytes, and finally the checksum.
!
       MAP1 WHOLE'BLOCK,X
               MAP2 SOH,X,1,CHR$(1)
               MAP2 BLOCK'NUMBER,B,1,1
               MAP2 NOT'BLOCK'NUMBER,B,1
               MAP2 IO'BLOCK,X,128
               MAP2 CHECK'SUM'BYTE,X,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'BLOCK
               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 BLOCK'LENGTH,F,,128
       MAP1 IN'CHR,X,1
       MAP1 TEST'CHR,X,1
       MAP1 OUT'CHR,X,1
       MAP1 DISK'FSPEC,S,12
       MAP1 OUT'UNIT,F,,0
       MAP1 CHECK'SUM,F
       MAP1 CTRLC,X,1,CHR$(3)
       MAP1 DEL,X,1,CHR$(255)
       MAP1 EOT,X,1,CHR$(4)
       MAP1 ACK,X,1,CHR$(6)
       MAP1 BELL,X,1,CHR$(7)
       MAP1 NAK,X,1,CHR$(21)
       MAP1 ESCAPE,X,1,CHR$(27)
       MAP1 I,F
       MAP1 NUM'BLOCKS,F
       MAP1 COUNT'BLOCKS,F
       MAP1 GOT'NAK,F
       MAP1 DEBUG,F
       MAP1 TRUE,F,,-1
       MAP1 FALSE,F,,0
       MAP1 TIMER,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
       MAP1 CHOICE,F
       MAP1 AREA,X,616                 ! for FILEIN
       MAP1 DRAIN'BUF,X,20
       MAP1 LENGTH,F                   ! from FILEIN and TIMEIN
       MAP1 TIMEOUT,F
       MAP1 NAK'COUNT,F
       MAP1 TIMEOUT'CNT,F
       MAP1 START'TIME,F

GET'FILENAME:
       DISK'FSPEC=""
       INPUT LINE "Name of file to be transmitted:  ",DISK'FSPEC
       IF DISK'FSPEC="" GOTO GET'FILENAME
       IF UCS(DISK'FSPEC[-2,-1])="/D" THEN &
               DISK'FSPEC=DISK'FSPEC[1,-3] : &
               OPEN #99,"MTRANS.LST",OUTPUT : &
               DEBUG=TRUE
       DISK'FSPEC=UCS(DISK'FSPEC)
       I=INSTR(1,DISK'FSPEC,".")
       IF I=0 THEN DISK'FSPEC=DISK'FSPEC+".MAC"
       LOOKUP DISK'FSPEC,I
       IF I=0 THEN &
               CALL TRY'AGAIN : &
               GOTO GET'FILENAME
       XCALL IMG
       XCALL FILEIN,1,AREA,DISK'FSPEC
!       OPEN #OUT'UNIT, "TRM:", OUTPUT
!
!       Transfer starts with sender (us) sending "ESC a" sequence to the
!       receiver, then wait for an ACK.  Then the first block is sent
!       in standard XMODEM block format.  Then we wait for an ACK.  Then
!       we send an EOT and wait for an ACK.  NOTE:  since for some
!       unfathomable reason the current version of MacTerminal (1.1) will
!       not retransmit the file information block if a NAK is received,
!       we will duplicate that behavior here.
!
       OUT'CHR=ESCAPE
       CALL SEND'ONE
       OUT'CHR="a"
       CALL SEND'ONE
       CALL WAIT'ACK
       IF GOT'NAK GOTO CLOSE'UP
       IF NOT TIMEOUT GOTO NT1
       IF DEBUG THEN &
               ?#99,"Timed out waiting for ACK after header"
       GOTO TIMEOUT
NT1:
       XCALL FILEIN,2,AREA,IO'BLOCK,LENGTH
       IF LENGTH#BLOCK'LENGTH GOTO BAD'FILE
       TIMER=TIME+2
WAIT:
       IF TIME<TIMER GOTO WAIT
       CALL SEND'BLOCK
       IF GOT'NAK GOTO CLOSE'UP
       CALL SEND'EOT
!
!       Now we send the data and resource forks, in that order.  Each
!       fork is sent using the complete XMODEM protocol.  That means that
!       we have to first wait for a NAK from the receiver.  Then we send
!       each block as SOH, block #, inverse block #, 128 data bytes, and
!       a one byte checksum.  Then we wait for an ACK; if we get a NAK,
!       we retransmit the block.  After the last block (of each fork) we
!       send an EOT and wait for an ACK.  That's all folks!  Note one
!       little thing--the block count IS reset at the beginning of each fork.
!
       TEMP1=DSIZE
       CALL MASSAGE
       SIZED=TEMP'SIZE
       TEMP1=RSIZE
       CALL MASSAGE
       SIZER=TEMP'SIZE
       SIZE=SIZED
       CALL SEND'SEGMENT
       SIZE=SIZER
       CALL SEND'SEGMENT
       XCALL FILEIN,3,AREA
       GOTO CLOSE'UP
!
SEND'SEGMENT:
       CALL GET'ONE
       IF NOT TIMEOUT GOTO NT2
       IF DEBUG THEN &
               ?#99,"Timed out waiting for NAK at beginning of fork"
       GOTO TIMEOUT
NT2:
       IF IN'CHR=CTRLC THEN &
               CALL CTRLC'ABORT : &
               GOTO SEND'SEGMENT
       IF IN'CHR=DEL THEN &
               CALL DEL'ABORT : &
               GOTO SEND'SEGMENT
       IF IN'CHR#NAK GOTO SEND'SEGMENT
       BLOCK'NUMBER=1
       NUM'BLOCKS=INT(SIZE/BLOCK'LENGTH)
       IF NUM'BLOCKS*BLOCK'LENGTH#SIZE THEN NUM'BLOCKS=NUM'BLOCKS+1
       IF NUM'BLOCKS=0 GOTO NO'DATA
       FOR COUNT'BLOCKS=1 TO NUM'BLOCKS
               XCALL FILEIN,2,AREA,IO'BLOCK,LENGTH
               IF LENGTH#BLOCK'LENGTH GOTO BAD'FILE
               NAK'COUNT=0
               TIMEOUT'CNT=0
       RETRY'BLOCK:
               CALL SEND'BLOCK
               IF GOT'NAK THEN &
                       NAK'COUNT=NAK'COUNT+1 : &
                       IF NAK'COUNT=5 GOTO CLOSE'UP &
                       ELSE GOTO RETRY'BLOCK
               IF NOT TIMEOUT GOTO NT3
               TIMEOUT'CNT=TIMEOUT'CNT+1
               IF DEBUG THEN &
                       ?#99,"Timed out waiting for ACK after this block"
               IF TIMEOUT'CNT=3 GOTO TIMEOUT
               GOTO RETRY'BLOCK
       NT3:
               BLOCK'NUMBER = BLOCK'NUMBER+1
       NEXT COUNT'BLOCKS
NO'DATA:
       CALL SEND'EOT
       RETURN

SEND'BLOCK:

       NOT'BLOCK'NUMBER=NOT BLOCK'NUMBER

       CHECK'SUM = 0
!       FOR I = 1 TO BLOCK'LENGTH
!               CHECK'SUM = CHECK'SUM+DATA'BYTES(I)
!       NEXT
!       CHECK'SUM'BYTE=CHR$(CHECK'SUM AND 255)
       XCALL CMPCRC,IO'BLOCK,CHECK'SUM'BYTE

       IF DEBUG THEN &
               ?#99,STR(ASC(SOH)) : &
               ?#99,STR(BLOCK'NUMBER) : &
               ?#99,STR(NOT'BLOCK'NUMBER) : &
               ?#99,"DATA BLOCK" : &
               ?#99,STR(ASC(CHECK'SUM'BYTE))
       PRINT #OUT'UNIT,WHOLE'BLOCK;
       CALL DRAIN
       CALL WAIT'ACK
       RETURN

DRAIN:
       XCALL TIMEIN,0,DRAIN'BUF,LENGTH
       IF LENGTH=20 GOTO DRAIN
       RETURN

WAIT'ACK:
       GOT'NAK=FALSE
       CALL GET'ONE
       IF TIMEOUT RETURN
       IF IN'CHR = CTRLC THEN &
               CALL CTRLC'ABORT : &
               GOTO WAIT'ACK
       IF IN'CHR=DEL THEN &
               CALL DEL'ABORT : &
               GOTO WAIT'ACK
       IF IN'CHR = NAK THEN &
               GOT'NAK=TRUE : &
               RETURN
       IF IN'CHR <> ACK GOTO WAIT'ACK
       RETURN

SEND'EOT:
       OUT'CHR=EOT
       CALL SEND'ONE
       CALL WAIT'ACK
       IF GOT'NAK GOTO SEND'EOT
       IF NOT TIMEOUT RETURN
       IF DEBUG THEN &
               ?#99,"Timed out waiting for ACK after EOT"
       GOTO TIMEOUT

MASSAGE:
       FOR I=1 TO 4
       TEMP2X(5-I)=TEMP1X(I)
       NEXT
       RETURN

GET'ONE:
       START'TIME=TIME
       TIMEOUT=FALSE
       XCALL TIMEIN,60,IN'CHR,LENGTH
       IF DEBUG AND LENGTH#0 THEN &
               ?#99,"-";STR(ASC(IN'CHR))
       IF LENGTH=0 THEN &
               TIMEOUT=TRUE : &
               IF DEBUG THEN ?#99,"Time=";STR(TIME-START'TIME)
       RETURN

SEND'ONE:
       ?#OUT'UNIT,OUT'CHR;
       IF DEBUG THEN &
               ?#99,STR(ASC(OUT'CHR))
       RETURN

DEL'ABORT:
       TEST'CHR=DEL
       GOTO TEST'ABORT
CTRLC'ABORT:
       TEST'CHR=CTRLC
TEST'ABORT:
       FOR I=1 TO 2
       XCALL TIMEIN,2,IN'CHR,LENGTH
       IF LENGTH=0 OR IN'CHR#TEST'CHR RETURN
       NEXT
       ?"Ending due to operator interrupt!"
       GOTO CLOSE'UP

TIMEOUT:
       ?"Sorry, your Mac isn't talking to me--ending."
       GOTO CLOSE'UP

BAD'FILE:
       GOTO CLOSE'UP

CLOSE'UP:
       IF DEBUG THEN CLOSE #99
       END

!===================================================================
TRY'AGAIN:
               ? "That file --- "DISK'FSPEC " --- could not be found. "
               ?
               ? "Would you like to: "
               ? "                   1 -- Try Again! "
               ? "                   2 -- Return to the Menu."
               INPUT " (enter the number of your choice ---  > ", CHOICE
               IF CHOICE=1 RETURN
               IF CHOICE#2 GOTO TRY'AGAIN
               END