separate (NETWORK_PROTOCOLS) package body XMODEM_PROTOCOL is package UTILITIES is new PROTOCOL_INDEPENDENT_UTILITIES ( BYTE, F_READ, F_WRITE, RS_READ, RS_WRITE, RS_WRITE_FLUSH, SERVICE ) ; use UTILITIES ; NUL : constant Byte := Byte'Val ( Character'Pos ( ASCII.NUL ) ) ; SOH : constant Byte := Byte'Val ( Character'Pos ( ASCII.SOH ) ) ; EOT : constant Byte := Byte'Val ( Character'Pos ( ASCII.EOT ) ) ; ACK : constant Byte := Byte'Val ( Character'Pos ( ASCII.ACK ) ) ; NAK : constant Byte := Byte'Val ( Character'Pos ( ASCII.NAK ) ) ; Resplim : constant Duration := 10.0 ; Charlim : constant Duration := 1.0 ; Clearlim : constant Duration := 5.0 ; Errlim : constant Integer := 10 ; Data : Byte_String ( 1 .. 128 ) ; procedure Update_For_Next_Block ( Block_Num : in out Integer ; Complement : in out Integer ; Error_Cnt : in out Integer ; L_Block : in out Integer ; L_Comp : in out Integer ) is -- This procedure updates the block number, the complement of the block -- number, and the error count each time a block is both successfully -- transmitted and received. begin L_Block := Block_Num ; L_Comp := Complement ; Block_Num := Block_Num + 1 ; if Block_Num = 256 then Block_Num := 0 ; end if ; Complement := Complement - 1 ; if Complement = -1 then Complement := 256 ; end if ; Error_Cnt := 0 ; end Update_For_Next_Block ; procedure WAIT_FOR_CLEAR ( Error_Cnt : in out Integer ) is -- This procedure is called when a Hold_Error, Read_Error, or Blk_Error -- Exception is raised. It increments the error counter and then waits -- for a clear line so that the exception handler can send the correct response Hold : Byte ; Received_C : Boolean ; begin Error_Cnt := Error_Cnt + 1 ; loop RS_READ_WITH_TIMEOUT ( Hold, Received_C, Clearlim ) ; exit when not Received_C ; end loop ; end WAIT_FOR_CLEAR ; procedure NEW_BLOCK ( Error_Cnt : in out Integer ; Hold : out Byte ) is -- This procedure is called any time the start of a new block is expected. -- The file transfer error exception is raised if the error limit is reached. Received_C : Boolean ; begin RS_READ_WITH_TIMEOUT ( Hold, Received_C, Resplim ) ; if Received_C then return ; end if ; Error_Cnt := Error_Cnt + 1 ; Write_Read_With_Expected_Response ( Error_Cnt, Errlim, Hold, NAK, Resplim, SOH ) ; if Error_Cnt = Errlim then raise FILE_TRANSFER_ERROR ; end if ; end NEW_BLOCK ; procedure LOOK_FOR_RESPONSE ( Error_Cnt : in out Integer ; Hold : out Byte ; Write : in Byte ; Response : in Byte ) is -- This procedure is called by both sender and receiver when a specific -- response to the byte that is sent to the other is required. The file -- transfer error exception is raised if the error limit is reached. The -- error count is reset at the end if the expected responce is received. begin Write_Read_With_Expected_Response ( Error_Cnt, Errlim, Hold, Write, Resplim, Response ) ; if Error_Cnt = Errlim then raise FILE_TRANSFER_ERROR ; end if ; Error_Cnt := 0 ; end LOOK_FOR_RESPONSE ; procedure RECEIVE is Checksum : Byte ; Chksum : Byte ; Error_Cnt : Integer := 0 ; Block_Num : Integer := 1 ; Blk_Num : Integer := 0 ; Complement : Integer := 254 ; Comp : Integer := 0 ; L_Block : Integer := 1 ; L_Comp : Integer := 254 ; Last : Integer ; Hold : Byte ; Complete : Boolean ; Read_Error : Exception ; Blk_Error : Exception ; -- This procedure implements the receive portion of the XMODEM file -- transfer protocol. Specific comments will be found in the code. -- The transfer is started by sending a NAK to the sender and looking for a -- SOH as the answer to begin the transfer. begin LOOK_FOR_RESPONSE ( Error_Cnt, Hold, NAK, SOH ) ; -- send NAK, look for SOH loop begin if Hold = SOH then Get_Byte ( Hold, Charlim ) ; -- read the block number byte Blk_Num := Byte'Pos ( Hold ) ; -- get integer of block number Get_Byte ( Hold, Charlim ) ; -- read the complement byte Comp := Byte'Pos ( Hold ) ; -- get integer of complement -- Check the block number and the complement if Blk_Num /= Block_Num or Comp /= Complement then raise Blk_Error ; end if ; -- read the 128 bytes of data RS_READ_BLOCK_WITH_TIMEOUT ( Data, Charlim, Complete, Last ) ; if not Complete then raise Read_Error ; end if ; Get_Byte ( Checksum, Charlim ) ; -- read the checksum byte Chksum := Low_Byte_Of_Sum ( Data ) ; -- get integer of checksum if Chksum = Checksum then -- the block has no errors RS_WRITE ( ACK ) ; -- acknowlege receipt of block F_WRITE_BLOCK ( Data ) ; -- write block to file Update_For_Next_Block ( Block_Num, Complement, Error_Cnt, L_Block, L_Comp ) ; else -- block contains errors RS_WRITE ( NAK ) ; -- tell sender there were errors Error_Cnt := Error_Cnt + 1 ; end if ; elsif Hold = EOT then RS_WRITE ( ACK ) ; -- acknowlege receipt of end of transfer return ; else WAIT_FOR_CLEAR ( Error_Cnt ) ; RS_WRITE ( NAK ) ; end if ; NEW_BLOCK ( Error_Cnt, Hold ) ; -- look for SOH exception when Read_Error | Utilities.Read_Error => -- An error occurred when reading one of the 128 data bytes. WAIT_FOR_CLEAR ( Error_Cnt ) ; RS_WRITE ( NAK ) ; -- send NAK to indicate an error NEW_BLOCK ( Error_Cnt, Hold ) ; -- look for SOH when Blk_Error => WAIT_FOR_CLEAR ( Error_Cnt ) ; if Blk_Num = L_Block and Comp = L_Comp then RS_WRITE ( ACK ) ; Error_Cnt := Error_Cnt - 1 ; else RS_WRITE ( NAK ) ; end if ; NEW_BLOCK ( Error_Cnt, Hold ) ; when Utilities.File_Transfer_Error => raise File_Transfer_Error ; end ; end loop ; end RECEIVE ; procedure SEND is Checksum : Byte ; Error_Cnt : Integer := 0 ; Received_C : Boolean ; First : Boolean := True ; End_of_File: Boolean := False ; Block_Num : Integer := 1 ; Complement : Integer := 254 ; Last : Integer ; L_Block : Integer ; L_Comp : Integer ; Hold : Byte ; -- This procedure implements the send portion of the XMODEM file -- transfer protocol. The transfer is initiated by the receiver sending -- an ACK begin LONG_READ ( Error_Cnt, Hold, Errlim, Resplim ) ; Error_Cnt := 0 ; loop if Hold = ACK or First then -- first block or last one was fine if First then First := False ; else Update_For_Next_Block ( Block_Num, Complement, Error_Cnt, L_Block, L_Comp ) ; end if ; if End_of_File then -- last block sent, send end of transmission Error_Cnt := 0 ; LOOK_FOR_RESPONSE ( Error_Cnt, Hold, EOT, ACK ) ; return ; end if ; F_READ_BLOCK ( Data, End_of_File, Last ) ; -- Read a 128 byte block elsif Error_Cnt = Errlim then raise FILE_TRANSFER_ERROR ; else Error_Cnt := Error_Cnt + 1 ; end if ; RS_WRITE ( SOH ) ; -- send start of header RS_WRITE ( Byte'Val ( Block_Num ) ) ; -- send block number RS_WRITE ( Byte'Val ( Complement ) ) ; -- send complement RS_WRITE_BLOCK ( Data ) ; -- send 128 byte block of data Checksum := Low_Byte_Of_Sum ( Data ) ; -- calculate checksum RS_WRITE ( Checksum ) ; -- send checksum RS_READ_WITH_TIMEOUT ( Hold, Received_C, Resplim ) ; -- look for ACK or NAK if not Received_C then Hold := NAK ; end if ; end loop ; end SEND ; end XMODEM_PROTOCOL ;