; Modified 8/24/85 for use with QuickBasic Compiler

; Heavy modifications 8/31/86 by Jim King
; Changed CRC_CALC from the awfulness it was to an algorithm suggested
; by Philip Burns.  In a test program, this algorithm is over 3 times as
; fast as the one previously used by RBBS-PC.
; Changed the loop that calculates checksum and calls the CRC to be more
; efficient (just about halved the number of instructions).
; Note that RBBS-PC.BAS was also modified so that it no longer tacks on
; two null bytes to the input string (they were necessary for the old CRC
; routine to work correctly).
; Once again, thanks to Philip Burns for suggesting the CRC algorithm.
; Many thanks also to John Souvestre, who helped me tweak the assembly
; routine to run even faster.

XM_CALC   SEGMENT PUBLIC 'CODE'
          ASSUME CS:XM_CALC
          PUBLIC XMODEM
;
CHK_SUM           DB 0
STRG_LEN          DW 0                  ;CHANGED TO LENGTH OF STRING PASSED
STRG_LOC          DW 0
STRG_MSG          DB 1026 DUP (' ')     ;COMMAND CHARS (+CR) GO INTO HERE
;
;
;
XMODEM    PROC    FAR
          PUSH    BP
          MOV     BP,SP
          MOV     CHK_SUM,0         ;INITIALIZE
;
          MOV     SI,[BP+14]        ;GET STRING DESCRIPTOR
          MOV     BL,[SI+ 2]        ;REARRANGE LOW/HIGH BYTES
          MOV     BH,[SI+ 3]        ;NOW BX HOLDS THE ADDRESS OF THE STRING
          MOV     STRG_LOC,BX       ;STORE IT
          MOV     AX,[SI]           ;GET STRING LENGTH
          MOV     STRG_LEN,AX       ;STORE IT
;
          MOV     CX,STRG_LEN           ;STORE LENGTH IN CX
          MOV     SI,STRG_LOC           ;STORE OFFSET TO STRING IN SI
          PUSH    CS
          POP     ES
          MOV     DI,OFFSET STRG_MSG    ;ES:DI = LOCATION OF VARIABLE
          REP     MOVSB                 ;FILL STRG_MSG WITH STRING
;
          PUSH    DS                    ;SAVE DS
          PUSH    CS
          POP     DS

          MOV     CX,STRG_LEN           ;INITIALIZE COUNTER
	  MOV	  SI,OFFSET STRG_MSG    ;get address of input string
          XOR     DX,DX			;initialize CRC value to 0
LOOP1:
	  LODSB				;get character into AL
          MOV     DI,CX                 ;SAVE CX
          ADD     CHK_SUM,AL            ;ADD AL TO CHK_SUM

; this used to be:
;CRC_CALC   PROC NEAR
; this is the CRC calculation routine.  It's placed here instead of in
; a separate procedure for additional speed.
; DX contains the CRC value, AL has the new character.  Other registers
; are used for temporary storage and scratch work.
	XCHG	DH,DL			; CRC := Swap(CRC) XOR Ord(Ch);
	XOR	DL,AL

	MOV	AL,DL			; CRC := CRC XOR ( Lo(CRC) SHR 4 );
	MOV	CL,4
	SHR	AL,CL
	XOR	DL,AL

					; CRC := CRC XOR ( Swap(Lo(CRC)) SHL 4 )
					;        XOR ( Lo(CRC) SHL 5 );
	MOV	BL,DL
	MOV	AH,DL
	SHL	AH,CL
	XOR	DH,AH
	XOR	BH,BH
	INC	CL
	SHL	BX,CL
	XOR	DX,BX
; end of the CRC calculation routine
	
          MOV     CX,DI                 ;RESTORE CX
	  LOOP	  LOOP1			;do it again


          POP     DS                   ;RESTORE DS
          MOV     BX,DX                ;PASS BACK THE CRC VALUE
          MOV     SI,[BP+ 6]           ;AND CRC HIGH AND LOW BYTES
          MOV     [SI],BL
          MOV     SI,[BP+ 8]
          MOV     [SI],BH
          MOV     SI,[BP+10]
          MOV     [SI],BX
          MOV     BL,CS:CHK_SUM        ;PASS BACK THE CHECK SUM
          MOV     SI,[BP+12]
          MOV     [SI],BL
;
          PUSH    CS                ;CLEAN UP WORK TO RETURN TO BASIC
          POP     ES
          POP     BP
          RET     10
XMODEM    ENDP
XM_CALC   ENDS
          END
