;*************************** AMUS Program Label ******************************
; Filename: XM.M68                                          Date: 07/15/91
; Category: UTIL         Hash Code: 736-155-127-460      Version: 4.0(100)
; Initials: ULTR/US      Name: DAVID PALLMANN
; Company: ULTRASOFT CORPORATION                   Telephone #: 5163484848
; Related Files:
; Min. Op. Sys.: AMOSL 1.3B/AMOS32 1.0         Expertise Level: INT
; Special: Replaces X.LIT to avoid conflict with other programs named X
; Description: XMODEM slave program - see XM.WRT for definition of protocol
;              This program is also distributed as part of TALKTO 4.0.
;              Copyright (C) 1991 UltraSoft Corporation
;*****************************************************************************

;****************************************************************************
;*                                                                          *
;*                                   XM                                     *
;*                          XMODEM slave program                            *
;*                                                                          *
;****************************************************************************
;Copyright (C) 1991 UltraSoft Corporation.  All Rights Reserved.
;
;Written by: David Pallmann
;
;Edit History:
;[100]  4.0(100)  Created. /DFP

       VMAJOR  =4
       VMINOR  =0
       VSUB    =0
       VEDIT   =100.
       VWHO    =0

;universals

       SEARCH  SYS
       SEARCH  SYSSYM
       SEARCH  TRM

;error codes

       ER$BLK  =1                      ; block number wrong
       ER$COM  =2                      ; complemented block number wrong
       ER$CRC  =3                      ; CRC wrong

;ASCII character definitions

       SOH     =1                      ; Start of Header
       EOT     =4                      ; End of Transmission
       ACK     =6                      ; ACKnowledge
       CR      =13.                    ; Carriage Return
       NAK     =21.                    ; Negative AcKnowledge

;communications parameters

       BLKSIZ  =128.                   ; data packet size (bytes)
       RETRY   =10.                    ; max retry count

;XMODEM communications block structure

       .OFINI
       .OFDEF  X.SOH,  1               ; SOH byte (or EOT on end of file)
       .OFDEF  X.BLK,  1               ; block number
       .OFDEF  X.COM,  1               ; complemented block number
       .OFDEF  X.DAT,  BLKSIZ          ; data area
       .OFDEF  X.CRC,  1               ; CRC byte
       .OFSIZ  X.SIZ

;variables

       .OFINI
       .OFDEF  FILE,   D.DDB           ; file DDB
       .OFDEF  DATA,   X.SIZ           ; data packet
       .OFDEF  RETCNT, 2               ; retry count
       .OFDEF  ERRCOD, 1               ; error code
       .OFDEF  BINFLG, 1               ; binary flag (0=text, 1=binary)
       .OFSIZ  MEMSIZ

;***********
;*  START  *
;***********

START:  PHDR    -1,0,PH$REE!PH$REU      ; program header
       GETIMP  MEMSIZ,A5               ;
       LEA     A4,DATA(A5)             ;

;set terminal to data mode and turn off echoplex

SETTRM: TRMRST  D0                      ;
       ORW     #T$DAT!T$ECS,D0         ;
       TRMWST  D0                      ;

;************
;*  CMDLIN  *
;************
;command line processing

CMDLIN: BYP                             ;
       LIN                             ;
       JEQ     USAGE                   ;
       MOVB    (A2)+,D1                ;
       UCS                             ;
10$:    ALF                             ;
       BNE     20$                     ;
       INC     A2                      ;
       BR      10$                     ;
20$:    BYP                             ;
       CMPB    D1,#'S                  ;
       JEQ     SEND                    ;
       CMPB    D1,#'R                  ;
       JEQ     RECEIVE                 ;

FMTERR: TYPECR  ?Command format error   ;
       CRLF                            ;

USAGE:  TYPESP  UltraSoft XMODEM slave program
       VCVT    START+2,OT$TRM          ;
       CRLF                            ;
       CRLF                            ;
       TYPECR  <Usage: .XM SEND filespec>
       TYPECR  <       .XM RECEIVE filespec>
       CRLF                            ;
       EXIT                            ;

;**********
;*  SEND  *
;**********
;send file

SEND:   FSPEC   FILE(A5)                ;
       INIT    FILE(A5)                ;
       OPENI   FILE(A5)                ;
       CLR     D4                      ; block number
       TYPECR  [ Ready to send file ]  ;

S.RDY:  CALL    $GTBYT                  ;
       CMPB    D1,#NAK                 ;
       BEQ     S.GET                   ;
       CMPB    D1,#EOT                 ;
       JEQ     S.CLS                   ;
       BR      S.RDY                   ;

S.GET:  TST     FILE+D.SIZ(A5)          ; S.EOF?
       JEQ     S.EOF                   ;   yes
       INCB    D4                      ; update block number
       LEA     A0,X.DAT(A4)            ; index data area
       MOV     #BLKSIZ,D0              ;
10$:    FILINB  FILE(A5)                ;
       TST     FILE+D.SIZ(A5)          ;
       JEQ     S.PAD                   ;
       MOVB    D1,(A0)+                ;
       SOB     D0,10$                  ;
       BR      S.CRC                   ;

S.PAD:  MOVB    #'Z-'@,(A0)+            ;
       DEC     D0                      ;
       BEQ     S.CRC                   ;
10$:    CLRB    (A0)+                   ;
       SOB     D0,10$                  ;

S.CRC:  CLR     D5                      ;
       LEA     A0,X.DAT(A4)            ; index data area
       MOV     #BLKSIZ,D0              ;
       CLR     D1                      ;
10$:    MOVB    (A0)+,D1                ;
       ADDW    D1,D5                   ;
       SOB     D0,10$                  ;

S.SND:  MOVB    #SOH,D1                 ; SOH
       CALL    $PTBYT                  ;
       MOVB    D4,D1                   ; block number
       CALL    $PTBYT                  ;
       COM     D1                      ; complement of block number
       CALL    $PTBYT                  ;
       LEA     A0,X.DAT(A4)            ; index data area
       MOV     #BLKSIZ,D0              ;
10$:    MOVB    (A0)+,D1                ;
       CALL    $PTBYT                  ;
       SOB     D0,10$                  ;
       MOVB    D5,D1                   ; CRC
       CALL    $PTBYT                  ;

S.WAT:  CALL    $GTBYT                  ;
       CMPB    D1,#ACK                 ;
       JEQ     S.ACK                   ;
       CMPB    D1,#NAK                 ;
       JEQ     S.NAK                   ;
       JMP     S.WAT                   ;

S.ACK:  CLR     RETCNT(A5)              ; clear retry count
       JMP     S.GET                   ;

S.NAK:  INCW    RETCNT(A5)              ; increment retry count
       CMPW    RETCNT(A5),#RETRY       ;
       JHIS    S.ERR                   ;
       JMP     S.SND                   ;

S.EOF:  MOVB    #EOT,D1                 ;
       CALL    $PTBYT                  ;
       CALL    $GTBYT                  ; wait for final ACK

S.CLS:  CLOSE   FILE(A5)                ;
       CALL    $GTBYT                  ;
       TYPECR  [ File Transmitted Succesfully ]
       EXIT                            ;

S.ERR:  CLOSE   FILE(A5)                ;
10$:    CALL    $GTBYT                  ;
       CMPB    D1,#CR                  ;
       BNE     10$                     ;
       TYPECR  [ Maximum Number of Retries Exceeded ]
       EXIT                            ;

;*************
;*  RECEIVE  *
;*************
;receive file

RECEIVE:
       FSPEC   FILE(A5)                ;
       INIT    FILE(A5)                ;
       LOOKUP  FILE(A5)                ;
       BNE     10$                     ;
       DSKDEL  FILE(A5)                ;
10$:    OPENO   FILE(A5)                ;
       MOV     #1,D4                   ; block number
       TYPECR  [ Ready to receive file ]

R.RDY:  MOVB    #NAK,D1                 ; send NAK to indicate we are ready
       CALL    $PTBYT                  ;
       MOV     #10.*100.,D0            ; 10 second wait loop
10$:    CALL    $CKINP                  ;
       BEQ     R.GET                   ;
       SLEEP   #100.                   ;
       SOB     D0,10$                  ;
       BR      R.RDY                   ;

;get block of data
;wait for initial SOH or EOT

R.GET:  MOV     #10.*100.,D0            ; set 10 second loop count
5$:     CALL    $CKINP                  ; character received?
       BEQ     6$                      ;   yes
       SLEEP   #100.                   ;   no - sleep 1/100 second
       SOB     D0,5$                   ; loop until time-out or char rcvd
       JMP     R.NAK                   ; send an ACK
6$:     CALL    $GTBYT                  ; get character
       CMPB    D1,#SOH                 ; SOH?
       BEQ     R.RCV                   ;   yes - start of a comm. block
       CMPB    D1,#EOT                 ; EOT?
       JEQ     R.EOF                   ;   yes - end of file
       BR      R.GET                   ; discard byte and wait for another

;we've gotten the start of a communications block - receive the rest of it

R.RCV:  MOV     #1+2+BLKSIZ+1,D2        ; set block size loop count
       LEA     A0,DATA(A5)             ; index comm. block storage area
       BR      40$                     ; go store SOH we just received
10$:    MOV     #10.*100.,D0            ; 10 second wait loop
20$:    CALL    $CKINP                  ; character received?
       BEQ     30$                     ;   yes
       SLEEP   #100.                   ; no sleep 1/100 second
       SOB     D0,20$                  ; loop until timeout exceed or char rcvd
       CMP     D2,#1+2+BLKSIZ+1        ; did we get anything?
       JEQ     R.NAK                   ;   no - send NAK
       CMPB    X.SOH(A4),#EOT          ; did we get an EOT?
       JEQ     R.EOF                   ;   yes - end of file
       JMP     R.NAK                   ; send NAK
30$:    CALL    $GTBYT                  ; get character
40$:    MOVB    D1,(A0)+                ; store it
       SOB     D2,10$                  ; loop until entire block received

;we've gotten a full data block

R.CHK:  MOVB    #ER$BLK,ERRCOD(A5)      ;
       CMPB    D4,X.BLK(A4)            ; right block number?
       JNE     R.NAK                   ;   no - send NAK
       MOVB    #ER$COM,ERRCOD(A5)      ;
       MOV     D4,D7                   ; copy block number
       COM     D7                      ;   and complement it
       CMPB    D7,X.COM(A4)            ;
       BEQ     R.CRC                   ;
       AND     #177,D7                 ;
       CMPB    D7,X.COM(A4)            ; right complemented block number?
       JNE     R.NAK                   ;   no - send NAK

;check CRC of block data

R.CRC:  MOVB    #ER$CRC,ERRCOD(A5)      ;
       LEA     A0,X.DAT(A4)            ; index data area of block
       MOV     #BLKSIZ,D0              ; load block size
       CLR     D5                      ; pre-clear CRC
       CLR     D1                      ; clear d1
10$:    MOVB    (A0)+,D1                ; get byte
       ADDW    D1,D5                   ; add to checksum
       SOB     D0,10$                  ; loop until done
       CMPB    D5,X.CRC(A4)            ; right checksum?
       BEQ     R.WRT                   ;
       MOVB    D5,D7                   ;
       AND     #177,D7                 ;
       CMPB    D7,X.CRC(A4)            ;
       JNE     R.NAK                   ;   no - send NAK

;write out data to file

R.WRT:

;check for ^Z and nulls at end of buffer

       TSTB    BINFLG(A5)              ; is this file text or binary?
       BNE     5$                      ;   binary - don't strip anything
       LEA     A6,X.DAT+BLKSIZ(A4)     ;
       MOV     #BLKSIZ-1,D0            ;
2$:     TSTB    -(A6)                   ;
       BNE     3$                      ;
       SOB     D0,2$                   ;
3$:     CMPB    @A6,#'Z-'@              ;
       BEQ     R.PAR                   ;

5$:     LEA     A0,X.DAT(A4)            ; index data area of block
       MOV     #BLKSIZ,D0              ; load block size
10$:    MOVB    (A0)+,D1                ; get byte
       CMPB    D1,#200                 ; 8-bit byte?
       BLO     20$                     ;   no
       MOVB    #1,BINFLG(A5)           ;   yes - set binary flag
20$:    FILOTB  FILE(A5)                ; write it out
       SOB     D0,10$                  ; loop
       BR      R.ACK                   ;

R.PAR:  DEC     D0                      ;
       BEQ     R.ACK                   ;
       LEA     A0,X.DAT(A4)            ;
10$:    MOVB    (A0)+,D1                ;
       FILOTB  FILE(A5)                ;
       SOB     D0,10$                  ;

;send ACK to acknowledge receipt of good data block

R.ACK:  CLRW    RETCNT(A5)              ; clear retry count
       MOVB    #ACK,D1                 ; send ACK (we like the block)
       CALL    $PTBYT                  ;
       INCW    D4                      ; update block number
       JMP     R.GET                   ; go get next block

;send NAK to reject block and indicate retransmission is necessary

R.NAK:  INCW    RETCNT(A5)              ;
       CMPW    RETCNT(A5),#RETRY       ;
       JHIS    R.ERR                   ;
       MOVB    #NAK,D1                 ; send NAK, we reject the block
       CALL    $PTBYT                  ;
       JMP     R.GET                   ; go get block again

;end of file - close file and exit

R.EOF:  MOVB    #ACK,D1                 ; send final ACK
       CALL    $PTBYT                  ;   back to sender
       CLOSE   FILE(A5)                ;
10$:    CALL    $GTBYT                  ;
       CMPB    D1,#CR                  ;
       BNE     10$                     ;
       TYPECR  [ File Received Succesfully ]
       EXIT                            ;

R.ERR:  CLOSE   FILE(A5)                ;
       DSKDEL  FILE(A5)                ;
10$:    CALL    $GTBYT                  ;
       CMPB    D1,#'D                  ;
       JEQ     DUMP                    ;
       CMPB    D1,#CR                  ;
       BNE     10$                     ;
       TYPECR  [ Maximum Number of Retries Exceeded ]

R.ERR2: TYPESP  [ Error code
       CLR     D1                      ;
       MOVB    ERRCOD(A5),D1           ;
       DCVT    0,OT$TRM!OT$TSP         ;
       TYPECR  ]                       ;
       TYPESP  [ Expected checksum
       MOV     D5,D1                   ;
       AND     #377,D1                 ;
       OCVT    0,OT$TRM!OT$TSP         ;
       TYPECR  ]                       ;
       EXIT                            ;

DUMP:   LEA     A0,DATA(A5)             ;
       MOV     #X.SIZ,D0               ;
       CLR     D1                      ;
10$:    MOVB    (A0)+,D1                ;
       OCVT    4,OT$TRM!OT$ZER         ;
       SOB     D0,10$                  ;
       CRLF                            ;
       CRLF                            ;
       JMP     R.ERR2                  ;

;************
;*  $GTBYT  *
;************
;Function:      Get byte from modem port (terminal version)
;
;Inputs:        none
;
;Outputs:       D1 - byte

$GTBYT: TIN                             ;
       RTN                             ;

;************
;*  $PTBYT  *
;************
;Function:      Put byte to modem port (terminal version)
;
;Inputs:        D1 - byte to send
;
;Outputs:       none

$PTBYT: TTY                             ;
       RTN                             ;

;************
;*  $CKINP  *
;************
;Function:      Check port for input
;
;Inputs:        none
;
;Outputs:       Z - set if there is input; cleared if there is no input

$CKINP: TCKI                            ; set Z if there is input else clear it
       RTN                             ; return with Z set or cleared

       END