; begin kermit5.asm [18]
 
;       Packet routines
 
; Send_Packet
; This routine assembles a packet from the arguments given and sends it
; to the host.
;
; Expects the following:
;       AH     - Type of packet (D,Y,N,S,R,E,F,Z,T)
;       ARGBLK - Packet sequence number
;       ARGBK1 - Number of data characters
; Returns: +1 always
 
SPKT	PROC	NEAR

spack:  push ax                 ; Save the packet type.
        mov bx,offset packet    ; Get address of the send packet.
        mov ah,soh              ; Get the start of header char.
        mov [bx],ah             ; Put in the packet.
        inc bx                  ; Point to next char.
        mov ax,argbk1           ; Get the number of data chars.
	xchg ah,al
        add ah,' '+3            ; Real packet character count made printable.
        mov [bx],ah             ; Put in the packet.
        inc bx                  ; Point to next char.
        mov cl,ah               ; Start the checksum.
        mov ax,argblk           ; Get the packet number.
	xchg ah,al
        add ah,' '              ; Add a space so the number is printable.
        mov [bx],ah             ; Put in the packet.
        inc bx                  ; Point to next char.
        add cl,ah               ; Add the packet number to the checksum.
        pop ax                  ; Get the packet type.
        mov [bx],ah             ; Put in the packet.
        inc bx                  ; Point to next char.
        add cl,ah               ; Add the type to the checksum.
        mov dx,argbk1           ; Get the packet size.
spack2: cmp dx,0                ; Are there any chars of data?
         jz spack3              ;  No, finish up.
        dec dx                  ; Decrement the char count.
        mov ah,[bx]             ; Get the next char.
        inc bx                  ; Point to next char.
        add cl,ah               ; Add the char to the checksum.
	cmp ah,0
	jns spack2
	mov hierr,0FFH		; set err flag. 
        jmp spack2              ; Go try again.
spack3: cmp hierr,0
	je sp3x			; Nothing special to do.
	call biterr
	mov hierr,0		; Reset.
sp3x:	mov ah,cl               ; Get the character total.
	mov ch,cl		; Save here too (need 'cl' for shift).
        and ah,0C0H             ; Turn off all but the two high order bits.
	mov cl,6
        shr ah,cl               ; Shift them into the low order position.
	mov cl,ch
        add ah,cl               ; Add it to the old bits.
        and ah,3FH              ; Turn off the two high order bits.  (MOD 64)
        add ah,' '              ; Add a space so the number is printable.
        mov [bx],ah             ; Put in the packet.
        inc bx                  ; Point to next char.
        mov ah,seol             ; Get the EOL the other host wants.
        mov [bx],ah             ; Put in the packet.
        inc bx                  ; Point to next char.
        mov ah,0                ; Get a null.
        mov [bx],ah             ; Put in the packet.
	cmp debug,0		; debug mode.
	je spack4
	inc bx
	mov ah,'$'
	mov [bx],ah
IF ibmpc	
	mov ah,2
	mov dx,scrsp
	mov bh,0
	int bios
ENDIF
IF Z100
	mov ah,prstr		; Get print string function
	mov dx,offset scrsp	; and string to move cursor
	int dos			; Print the string
ENDIF
	mov ah,prstr
	mov dx,offset spmes
	int dos
	mov dx,offset packet
	mov ah,prstr
	int dos			; debug end.
spack4: call outpkt             ; Call the system dependent routine.
	 jmp r
	jmp rskp
 
SPKT	ENDP 
;       Write out a packet.
 
OUTPKT  PROC    NEAR
        mov dh,spad             ; Get the number of padding chars.
outpk2: dec dh
        cmp dh,0
        jl outpk3               ; If none left proceed.
        mov ah,spadch           ; Get the padding char.
        call outchr             ; Output it.
        jmp outpk2
outpk3: mov bx,offset packet    ; Point to the packet.
outlup: mov ah,[bx]             ; Get the next character.
        cmp ah,0                ; Is it a null?
        jnz outlp2
        jmp rskp
outlp2: call outchr             ; Output the character.
	 jmp r
        inc bx                  ; Increment the char pointer.
        jmp outlup
OUTPKT  ENDP
 
 
;************************System Dependent****************************
;       Put a char in AH to the port.
PORT	PROC  NEAR 
IF ibmpc
outchr:	sub cx,cx		; [14 start]
	mov al,ah		; Parity routine works on AL. [16]
	call dopar		; Set parity appropriately.     [10]
	mov ah,al		; Don't overwrite character with status. [16]
	mov dx,03FDH
outch1:	in al,dx
	test al,20H		; Transmitter ready?
	jnz outch2		; Yes
	loop outch1
	 jmp r			; Timeout
outch2:	mov al,ah		; Now send it out
	mov dx,03F8H
	out dx,al
	jmp rskp		; [14 end]
ENDIF
IF Z100
outchr:	mov al,ah		; Yes, get the character into al
	mov cx,0
	call dopar		; Set parity appropriately.   [10]
outch1:	call bios_auxout	; Send the character
	jmp rskp
ENDIF
 
;************************System Dependent****************************
;
;       Get a char from the port and return in AH.
 
inchr:  
IF ibmpc
	call prtchr		; Get character if ready.  [14]
	 jmp inchr3		; Got it in AL.  [14]
ENDIF
IF Z100
 	push	bx		; Save BX
	mov ah,chr_status	; Get the function
	mov al,chr_sfgs		; And the subfunction
	call bios_auxfunc	; Determine if anything to input
	cmp bl,0		; Is there?
	jnz inchr3		; Yes, go get it
	pop bx			; And restore BX
ENDIF
        mov ah,constat          ; Is a char on the console?
        int dos
        cmp al,0
        jz inchr                ; If not go look for another char.
        mov ah,conin            ; Get the char.
        int dos
	mov ah,al 
        cmp ah,cr                       ; Is it a carriage return?
        je inchr4              ; If not go look for another char.
	jmp inchr		; Wait for some kind of input.
inchr4:	ret
inchr3: 
IF Z100
	mov ah,chr_read		; Get the function to read
	call bios_auxfunc	; Get a character
	pop bx			; Restore BX
ENDIF
	mov ah,al
	cmp parflg,parnon	; Is the parity none?   [10]
	je inchr5		; We're done.		[10]
	and ah,7FH                      ; Turn off the parity bit.
inchr5: jmp rskp

; Set parity for character in Register AL.

dopar:	cmp parflg,parnon	; No parity?			[10 start]
	je parret		; Just return
	cmp parflg,parevn	; Even parity?
	jne dopar0
	and al,07FH		; Strip parity.
	jpe parret		; Already even, leave it.
	or al,080H		; Make it even parity.
	jmp parret
dopar0:	cmp parflg,parmrk	; Mark parity?
	jne dopar1
	or al,080H		; Turn on the parity bit.
	jmp parret
dopar1:	cmp parflg,parodd	; Odd parity?	
	jne dopar2
	and al,07FH		; Strip parity.
	jpo parret		; Already odd, leave it.
	or al,080H		; Make it odd parity.
	jmp parret
dopar2: and al,07FH		; Space parity - turn off parity bit.
parret: ret							; [10 end]
PORT	ENDP
 
 
; Receive_Packet
; This routine waits for a packet arrive from the host.  It reads
; chars until it finds a SOH.

RPACK	PROC	NEAR
IF ibmpc
	mov ah,2
	mov dx,scrst		; Position cursor.
	mov bh,0
	int bios
ENDIF
IF Z100
	mov ah,prstr		; Get the function to print string
	mov dx,offset scrst	; Get the string address
	int dos			; Position the cursor
ENDIF
	mov ah,prstr
	mov dx,offset infms0
        int dos
rpack5: call inpkt              ; Read up to a carriage return.
         jmp r                  ;  Return bad.
rpack0: call getchr             ; Get a character.
         jmp r                  ;  Hit the carriage return, return bad.
        cmp ah,soh              ; Is the char the start of header char?
         jne rpack0             ;  No, go until it is.
rpack1: call getchr             ; Get a character.
         jmp r                  ;  Hit the carriage return, return bad.
        cmp ah,soh              ; Is the char the start of header char?
         jz rpack1              ;  Yes, then go start over.
        mov cl,ah               ; Start the checksum.
        sub ah,' '+3            ; Get the real data count.
        mov dh,ah               ; Save it for later.
        mov al,ah               ; Swap halves.
        mov ah,0
        mov argbk1,ax           ; Save the data count.
        call getchr             ; Get a character.
         jmp r                  ;  Hit the carriage return, return bad.
        cmp ah,soh              ; Is the char the start of header char?
         jz rpack1              ;  Yes, then go start over.
        add cl,ah               ; Add it to the checksum.
        sub ah,' '              ; Get the real packet number.
        mov al,ah               ; Swap halves.
        mov ah,0
        mov argblk,ax           ; Save the packet number.
        call getchr             ; Get a character.
         jmp r                  ;  Hit the carriage return, return bad.
        cmp ah,soh              ; Is the char the start of header char?
         jz rpack1              ;  Yes, then go start over.
        mov temp,ax             ; Save the message type. [11]
        add cl,ah               ; Add it to the checksum.
        mov bx,offset data      ; Point to the data buffer.
rpack2: dec dh                  ; Any data characters?
         js rpack3              ;  If not go get the checksum.
        call getchr             ; Get a character.
         jmp r                  ;  Hit the carriage return, return bad.
        cmp ah,soh              ; Is the char the start of header char?
         jz rpack1              ;  Yes, then go start over.
        mov [bx],ah             ; Put the char into the packet.
        inc bx                  ; Point to the next character.
        add cl,ah               ; Add it to the checksum.
        jmp rpack2              ; Go get another.
rpack3: call getchr             ; Get a character.
         jmp r                  ;  Hit the carriage return, return bad.
        cmp ah,soh              ; Is the char the start of header char?
         jz rpack1              ;  Yes, then go start over.
        sub ah,' '              ; Turn the char back into a number.
        mov dh,cl               ; Get the character total.
        and dh,0C0H             ; Turn off all but the two high order bits.
	mov ch,cl
	mov cl,6
        shr dh,cl               ; Shift them into the low order position.
	mov cl,ch
        add dh,cl               ; Add it to the old bits.
        and dh,3FH              ; Turn off the two high order bits.  (MOD 64)
        cmp dh,ah               ; Are they equal?
         jz rpack4              ;  If so finish up.
        ret
rpack4: mov ah,0
        mov [bx],ah             ; Put a null at the end of the data.
        mov ax,temp             ; Get the type.   [11]
        jmp rskp
RPACK   ENDP
 
 
INPKT   PROC    NEAR
        mov bx,offset recpkt    ; Point to the beginning of the packet.
	mov incnt,0
inpkt2: call inchr              ; Get a character.
         jmp r                  ;  Return failure.
        mov [bx],ah             ; Put the char in the packet.
        inc bx
	inc incnt
        cmp ah,reol             ; Is it the EOL char?
        jne inpkt2              ; If not loop for another.
	cmp incnt,1		; Ignore bare CR.   [2 start]
	jne inpkt6
	mov incnt,0
	mov bx,offset recpkt
	jmp inpkt2		;                   [2 end]
inpkt6:	cmp ibmflg,0            ; Is this the (dumb) IBM mainframe?
        jz inpkt4               ; If not then proceed.
inpkt5: cmp state,'S'           ; Check if this is the Send-Init packet.
        jz inpkt4               ; If so don't wait for the XON.
inpkt3: call inchr              ; Wait for the turn around char.
         jmp inpkt4
        cmp ah,xon              ; Is it the IBM turn around character?
        jne inpkt3              ; If not, go until it is.
inpkt4: mov bx,offset recpkt
        mov pktptr,bx           ; Save the packet pointer.
        jmp rskp                ; If so we are done.
INPKT   ENDP

 
GETCHR  PROC    NEAR
        push bx
        mov bx,pktptr           ; Get the packet pointer.
        mov ah,[bx]             ; Get the char.
        inc bx
        mov pktptr,bx
        pop bx                  ; Restore BX.
        cmp ah,reol             ; Is it the EOL char?
        jne getcr2              ; If not return retskp.
        ret                     ; If so return failure.
getcr2: jmp rskp
GETCHR  ENDP
 
 
; This is where we go if we get an error packet.  A call to ERROR 
; positions the cursor and prints the message.  A call to ERROR1
; just prints a CRLF and then the message.  [8]
 
ERROR   PROC    NEAR
        mov state,'A'           ; Set the state to abort.
        call erpos	        ; Position the cursor.
	jmp error2
error1:	mov ah,prstr
	mov dx,offset crlf
	int dos
error2: mov bx,argbk1           ; Get the length of the data.
        add bx,offset data      ; Get to the end of the string.
        mov ah,'$'              ; Put a dollar sign at the end.
        mov [bx],ah
        mov ah,prstr            ; Print the error message.
        mov dx,offset data
        int dos
        ret
ERROR   ENDP
 
; LOGOUT - tell remote KERSRV to logout.

LOGOUT	PROC	NEAR						; [8 start]
	mov ah,cmcfm
	call comnd		; Get a confirm.
	 jmp r
	call logo
	 jmp rskp		; Go get another command whether we ....
	jmp rskp		; .... succeed or fail.
LOGOUT	ENDP

LOGO	PROC	NEAR
	mov numtry,0		; Initialize count.
	call serini		; Initialize port.  [14]
logo1:	mov ah,numtry
	cmp ah,maxtry		; Too many times?
	js logo3		; No, try it.
logo2:	mov ah,prstr
	mov dx,offset erms19
	int dos
	call serrst		; Reset port.  [14]
	ret
logo3:	inc numtry		; Increment number of tries.
	mov argblk,0		; Packet number zero.
	mov argbk1,1		; One piece of data.
	mov bx,offset data
	mov ah,'L'
	mov [bx],ah		; Logout the remote host.
	mov ah,'G'		; Generic command packet.
	call spack
	 jmp logo2		; Tell user and die.
	 nop
	call rpack5		; Get ACK (w/o screen msgs.)
	 jmp logo1		; Go try again.
	 nop
	cmp ah,'Y'		; ACK?
	jne logo4
	call serrst		; Reset port.  [14]
	jmp rskp
logo4:	cmp ah,'E'		; Error packet?	
	jnz logo1		; Try sending the packet again.
	call error1
	call serrst		; Reset port.  [14]
	ret
LOGO	ENDP

; FINISH - tell remote KERSRV to exit.

FINISH	PROC	NEAR
	mov ah,cmcfm		; Parse a confirm.
	call comnd
	 jmp r
	mov numtry,0		; Initialize count.
	call serini		; Initialize port.  [14]
fin1:	mov ah,numtry
	cmp ah,maxtry		; Too many times?
	js fin3			; Nope, try it.
fin2:	mov ah,prstr
	mov dx,offset erms18
	int dos
	call serrst		; Reset port.  [14]
	jmp rskp		; Go home.
fin3:	inc numtry		; Increment number of tries.
	mov argblk,0		; Packet number zero.
	mov argbk1,1		; One piece of data.
	mov bx,offset data
	mov ah,'F'
	mov [bx],ah		; Finish running Kermit.
	mov ah,'G'		; Generic command packet.
	call spack
	 jmp fin2		; Tell user and die.
	 nop
	call rpack5		; Get ACK (w/o screen stuff).
	 jmp fin1		; Go try again.
	 nop
	cmp ah,'Y'		; Got an ACK?
	jnz fin4
	call serrst		; Reset port. [14]
	jmp rskp		; Yes, then we're done.
fin4:	cmp ah,'E'		; Error packet?
	jnz fin1		; Try sending it again.
	call error1
	call serrst		; Reset port.  [14]
	jmp rskp
FINISH	ENDP

; BYE command - tell remote KERSRV to logout & exits to DOS.  

BYE	PROC	NEAR
	mov ah,cmcfm		; Parse a confirm.
	call comnd
	 jmp r
	call logo		; Tell the mainframe to logout.
 	 jmp rskp		; Failed - don't exit.
	mov extflg,1		; Set exit flag.
	jmp rskp					; [8 end]
BYE	ENDP

; This is the 'exit' command.  It leaves KERMIT and returns to DOS.
 
EXIT    PROC    NEAR
        mov ah,cmcfm
        call comnd              ; Get a confirm.
         jmp r
        mov extflg,1            ;  Set the exit flag.
        jmp rskp                ; Then return to system.
EXIT    ENDP
 
 
; This is the 'help' command.  It gives a list of the commands.
 
HELP    PROC    NEAR
        mov ah,cmcfm
        call comnd              ; Get a confirm.
         jmp r
        mov ah,prstr            ; Print a string to the console.
        mov dx,offset tophlp    ; The address of the help message.
        int dos
        jmp rskp
HELP    ENDP
; end kermit5.asm [18]
tate,'B'           ; Set the state to EOT.
        ret
seof2:  cmp ah,'N'              ; NAK?
        jne seof3             