Category : Pascal Source Code
Archive   : XFER10.ZIP
Filename : SXRX.INC

 
Output of file : SXRX.INC contained in archive : XFER10.ZIP
{ The code in this file handles transmit and receive of Xmodem Checksum, }
{ CRC, and 1K. This is the first include file of XFER.PAS. }

{ Copyright (C) 1990 By Andrew Bartels }
{ A Product of Digital Innovations }


{ This function transmits via Xmodem protocol the file contained in }
{ FileName. If CRCMode is True, a CRC check is used on all blocks, else }
{ a checksum is used. If OneK is True, each data block is sent 1K long, }
{ else each data block is 128 bytes long. All errors and transfer results }
{ are returned through the function. These codes are defined in the }
{ opening Const definition at the beginning of XFER.PAS. }

Function XmodemSend( CRCMode,
OneK : Boolean;
Var FileName : String) : Byte;

Var Ch : Char; { Character just received }
TimeOut : Boolean; { Becomes True if a Timeout condition occurs }

NumNAKS : Integer; { Hold number of NAKs receiver has sent on }
{ current block. If this value exceeds 10, }
{ then receiver cancels protocol by sending }
{ two CAN's }

BlockNum, { Contains the current block being xmitted. }
{ Starts at 1, and increments by one until }
{ it becomes 255, at which point, it rolls }
{ over to 0, and continues incrementing. }

TotalErrors : Byte; { Stores total number of NAK's and other }
{ error conditions that have occurred thus }
{ far in the protocol, pending or not. }

TotalBlocks, { Contains the total number of blocks sent }
{ thus far. Similar to BlockNum, except this}
{ one does not roll over at 255. }

BytesSent : LongInt; { Keeps count of how many bytes of file data }
{ have been successfully sent thus far. }

Hour, { Holds Hour from GetTime call }
Min, { Holds Minute from GetTime call }
Sec, { Holds Second from GetTime call }
Hund : Word; { Holds Hundredths from GetTime call }

StartTime, { Holds time in seconds of when protocol was }
{ initiated. Used in determining Chars/Sec }

EndTime : Real; { Holds the time in seconds as when last blk }
{ was transmitted. Used with StartTime in }
{ figuring Char/Sec. }

Data : BlockType;{ This holds the data block to be xmitted. }

DataLen : Word; { Is 128 for a 128 byte block, or 1024 for a }
{ 1K block. }

CancelCode : Byte; { Contains the number of CAN's that came from}
{ receiver in a row. If this is ever 2, then}
{ protocol aborts. }

File1 : File; { File handle used for disk I/O }



{ This sub-procedure is the main instrument of disk I/O for xmit. }
{ In 1K transfers, data blocks returned from this procedure are 1K }
{ long until there is less than 1K of the file left to be sent, at }
{ which point 128 byte blocks are returned. In regular xfers, 128 byte}
{ data blocks are always returned. }

Procedure GetNextBlock (Var Data : BlockType;
Var DataLen : Word );
Begin
FillChar(Data,1024,#26); { all of block is set to ASCII code 26 }
{ for CP/M systems that require #26 at }
{ end of file. These will be written }
{ over a little later if this is not }
{ the last block we're sending. }

If (FileSize(File1) - FilePos(File1) >= 1024) and OneK
then {If it's OK to send a 1K block, then read in 1K of data }
Begin
BlockRead(File1,Data,1024,DataLen);
End
else
Begin { Otherwise, read in 128 bytes of data }
BlockRead(File1,Data,128,DataLen);
If (DataLen < 128) and (DataLen <> 0) { If we couldn't get }
then { 128 bytes due to EOF, then we }
DataLen := 128; { say block is 128 bytes anyway, }
{ knowing there are ASCII 26's }
{ on the end of the block to fill}
{ up the rest of it. }

End;
End;


Begin { OK, here's where we start the xfer }

If not Com_Carrier { If there is no carrier, then exit because we }
then { cannot send data. Correct error code is returned}
Begin
XmodemSend := CarrierLostError;
Exit;
End;

XmodemSend := NoError; { Assume no error has occurred yet.}

If not OpenFile(File1,Rd,1,FileName) { Try opening the file to read }
then
Begin
XmodemSend := FileError; { If we can't open the file, error }
Exit;
End;

Writeln('Sending Xmodem.....',FileName);

Repeat { Now we wait for the receiver to tell us whether protocol is}
{ CRC or Checksum. We'll wait no more than 30 seconds for it}
MonitorReceive(Ch,30,TimeOut);
{ We want to keep on waiting until we get a timeout, or info }
{ from the receiver. All garbage is discarded. }
Until (TimeOut) or
(not (TimeOut) and (Ch = #21)) or { Receiver want's checksum }
(not (TimeOut) and (CrcMode) and (Ch='C')); {Recv'r wants CRC }

If not Com_Carrier { If there is no carrier, then exit with error }
then
Begin
XmodemSend := CarrierLostError;
Close(File1);
Exit;
End;

If TimeOut { If we timed out waiting for receiver, then abort protocol }
then
Begin
Com_TX(CAN);
Com_TX(CAN);
XmodemSend := MaxTimeOutError;
Close(File1);
Exit;
End;

CRCMode := (Ch = 'C'); { If receiver sent a 'C', then we're going to }
{ do a CRC error check, else we'll used Csum. }

NumNAKs := 0; { No NAK's on blocks yet. }
TotalErrors := 0; { No errors yet. }

BytesSent := 0; { Didn't send anything yet }
TotalBlocks := 0; { ditto. }

GetTime(Hour,Min,Sec,Hund); { Figure out time to the hundredths of sec.}
StartTime := (3600 * Hour) + (60 * Min) + Sec + (Hund/100);

GotoXY(1,13); { Tell the user what's going on now. }
Writeln('FileName : ',FileName);
Writeln('File Size : ',FileSize(File1));
Write('Error Correction : ');
If CRCMode
then
Writeln('CRC-CCITT')
else
Writeln('Checksum');
Writeln('Block Number : ',0);
Writeln('Bytes Sent : ',0);
Writeln('Characters/Second : ',0);
Writeln('Elapsed Time : ',0,' min. ',0,' sec. ');
Writeln('Block Errors : ',0);
Writeln('Total Errors : ',0);

BlockNum := 1; { First block number is 1 }
TotalBlocks := 1; { We're sending first block now. }

Repeat
GetNextBlock(Data,DataLen); { Get block of data from file }
If DataLen > 0 { If not EOF in file, then..... }
then
Repeat

SendBlock(Data,DataLen,BlockNum,CRCMode); { ...send the block }

CancelCode := 0; { No CAN's received yet on this block }

Repeat
If KeyPressed { If user presses a key, error out }
then
Begin
XmodemSend := OperatorAbortError;
Close(File1);
Exit;
End;
If not Com_Carrier { If carrier drops, error out }
then
Begin
XmodemSend := CarrierLostError;
Close(File1);
Exit;
End;
MonitorReceive(Ch,10,TimeOut); {Wait for response from recv'r }

If (not TimeOut) and (Ch = CAN) { If receiver sends CAN, then }
then
Inc(CancelCode) { count it to see if we got 2 }
{ in a row yet. }
else
CancelCode := 0; { Otherwise we couldn't have }
{ gotten 2 in a row. }

{ Keep on looping until we either TimeOut, get an ACK, NAK, or }
{ 2 CAN's in a row. Discard all garbage. }
Until TimeOut or (Ch=ACK) or (Ch=NAK) or (CancelCode = 2);

Case Ch of { If we got a NAK, then count up the errors }
NAK : Begin
Inc(NumNAKs);
Inc(TotalErrors);
End;
ACK : Begin { If we got an ACK, then zero error count & go to }
{ next block. }
NumNAKs := 0;
BytesSent := BytesSent + DataLen;
End;
End;{Case}

{ Now, tell user how the xfer is going. }

GetTime(Hour,Min,Sec,Hund);
EndTime := (Hour*3600) + (Min * 60) + Sec + (Hund/100);
If EndTime < StartTime
then
EndTime := EndTime + (3600 * 24);
GotoXY(1,16);
Writeln('Block Number : ',TotalBlocks);
Writeln('Bytes Sent : ',BytesSent);
Writeln('Characters/Second : ',Trunc((BytesSent/(EndTime-StartTime)*100))/100:0:2);
Writeln('Elapsed Time : ',Trunc(EndTime-StartTime) div 60,' min. ',
Trunc(EndTime-StartTime) mod 60,' sec. ');
Writeln('Block Errors : ',NumNAKs);
Writeln('Total Errors : ',TotalErrors);

{ This loop goes until we either get an ACK, or 2 CAN's. This }
{ way, we will re-xmit the block if we got a NAK, or a timeout }
{ as the protocol definition describes. }

Until (Ch =ACK) or (CancelCode = 2);

Inc(BlockNum); { OK, set up for next block. }
Inc(TotalBlocks); { ditto }

{ This loop repeats for each data block, until DataLen is zero, which }
{ means the last block xmitted was the last in the file. The other }
{ way this loop exits is if we got 2 CAN's in a row from receiver. }

Until (DataLen=0) or (CancelCode = 2);

If CancelCode = 2 { If we got 2 CAN's, then display the error & exit }
then
Begin
XmodemSend := RemoteCancelError;
Close(File1);
Exit;
End;

Repeat { Now that EOF has been reached, we need to send EOT until the }
{ receiver ACKnowledges it. }
Com_TX(EOT);
MonitorReceive(Ch,10,TimeOut);
Until (TimeOut) or (Ch = ACK);

If TimeOut { If we timed out while sending the EOT, then issue an }
{ error. }
then
XmodemSend := MaxTimeOutError;

Close(File1); { Close the file, and exit }

End;


{ This function will receive a file by Xmodem protocol under the name }
{ specified in FileName. The receiver only needs to know if the user }
{ wants CRC transfers or not. This routine will handle either 1K or }
{ regular transfers automatically. It is recommended that if 1K blocks }
{ are used, the CRCMode be turned on. }

Function XmodemReceive( CRCMode : Boolean;
Var FileName : String ) : Byte;

Var Ch :Char; { Character just received }

TimeOut, { Becomes true when no character has been }
{ received for 10 seconds (a time-out }
{ condition). }

CRCOn, { True if CRC transfer, False if checksum }

Done, { True if protocol is to exit, either on }
{ cancel or EOT condition. }

StartUp, { Used to enter the main loop and not wait }
{ for a character from modem once CRC has }
{ been initiated. Is False if Checksum. }

ForceACK : Boolean; { Becomes true if block received = Last }
{ block sent. If True, block is ACK'd, }
{ but not saved to disk. }

CRC, { Holds current CRC value as data comes in }

Code, { Used to return an error code from IBMCom }
{ when we initialize the COM Port. }

BlockCRC, { Holds the CRC of the block, as sent by }
{ transmitter. This is compared with the }
{ CRC variable to determine if block is }
{ error-free. }

Status, { Status code for writing to disk. }

Hour, { Holds hour number (0-23) of timing code }
Min, { Holds minute number (0-59) in timing }
Sec, { Holds second number (0-59) in timing }
Hund : Word; { Holds 100th of a second in timing code }

Count, { Used in counting C's sent in attempt to }
{ initiate CRC mode. }

Operation, { The process of receiving an Xmodem block }
{ has been broken down into six different }
{ operations. These are shown in the large}
{ Case statement in the main loop. This }
{ variable is used to keep the steps in }
{ sequence. }

NumNAKS, { Counts number of NAK's that have been }
{ sent on this block. If it ever exceeds }
{ 10, then two CAN's are sent & protocol }
{ is aborted. }

DataNum, { Holds how many data bytes in the block }
{ have been received. This increments by }
{ one as each byte comes in, and stops }
{ when it equals MaxDataLength. }

MaxDataLength : Integer; { If first character of block is SOH, then }
{ this is 128. If first char is STX, then }
{ this is 1024, for 1K Xmodem support. }

CANNum, { Used to count how many CAN chars have }
{ been received in a row. Once two have }
{ been received in a row, protocol exits }
{ under a cancel condition. }

EOTNum, { Used to count how many EOT chars have }
{ been received in a row. Once two have }
{ been received in a row, protocol exits }
{ because transmission is complete. }

Checksum, { Used to hold the checksum of the data in }
{ checksum transfers. }

CRCBytes, { The CRC is two bytes long. In CRC mode, }
{ this is a counter to make sure both bytes}
{ of the CRC have been received from the }
{ transmitter. }


BlockNum, { Holds the current block number as sent }
{ by transmitter. Starts at 1, and }
{ increments to 255, then to 0, and starts }
{ counting up over again. }

TotalErrors, { Counter used to hold the total number of }
{ errors encountered thus far during the }
{ transfer. Used for screen information }
{ only. }

LastBlock : Byte; { Holds the last block's number. If this }
{ ever equals the value of BlockNum, then }
{ ForceACK is set to true. }

TotalBlocks, { Similar to BlockNum, except to does not }
{ wrap around to zero when it reaches 255 }
{ Used to display info for the user only. }

BytesReceived : LongInt; { Counts the total number of bytes received}
{ thus far in the transfer. Used to calc. }
{ the CPS field on the screen. }

StartTime, { Holds the time, in seconds, when the }
{ protocol was started. }

EndTime : Real; { Holds the current time, in seconds. Used}
{ with StartTime to determine how much time}
{ has passed since start of transfer, and }
{ to figure CPS rate. }

Data : BlockType; { Buffer used to hold actual }
{ file data as it is received. }
{ Later, it is written to disk }

File1 : File; { File handle for disk I/O }


{ This procedure is used only for displaying information to the user }
{ about how the xfer is going. There are a couple of places where }
{ this is needed, so it has been put into a procedure. }

Procedure UpdateVideo;
Begin
GetTime(Hour,Min,Sec,Hund); { Get the current time in seconds here }
EndTime := (Hour*3600) + (Min * 60) + Sec + (Hund/100);

If EndTime < StartTime { Allow for xfers spanning over midnight }
then
EndTime := EndTime + (3600 * 24);

GotoXY(1,15);
Writeln('Block Number : ',TotalBlocks);
Writeln('Bytes Received : ',BytesReceived);
Writeln('Characters/Second : ',Trunc((BytesReceived/(EndTime-StartTime)*100))/100:0:2);
Writeln('Elapsed Time : ',Trunc(EndTime-StartTime) div 60,' min. ',
Trunc(EndTime-StartTime) mod 60,' sec. ');
Writeln('Block Errors : ',NumNAKs);
Writeln('Total Errors : ',TotalErrors);
End;


{ There are many places in the receive routine where it is necessary to}
{ send a NAK character. This procedure sends the NAK, and updates the }
{ NAK counter so we know how many NAK's have been sent on this block. }
{ If the NAK counter exceeds 10, we immediately send two CAN's and exit}
{ the protocol due to exceeding the timeout maximum (when Operation is }
{ 200). }

Procedure SendNak;
Begin
Inc(NumNAKs); { Increment the counters by 1 }
Inc(TotalErrors);
If NumNAKs < 11 { If we didn't exceed the limit, send the NAK }
then
Com_TX(NAK);
Operation := 1; { Next operation is wait for next SOH char }
If NumNAKs = 11 { unless we met NAK limit, in which case we }
then { must exit the protocol after sending 2 CAN's}
Begin
Operation := 200;
XmodemReceive := MaxTimeOutError;
End;
GotoXY(1,19); { Tell user there was an error }
Writeln('Block Errors : ',NumNAKs);
Writeln('Total Errors : ',TotalErrors);
End;




Begin

If not Com_Carrier { If there is no carrier, then exit because we }
then { can't send anything like that. }
Begin
XmodemReceive := CarrierLostError;
Exit;
End;

XmodemReceive := NoError; { OK, assume there are no errors }

If not OpenFile(File1,Wt,1,FileName) { Try to open the file here }
then
Begin
XmodemReceive := FileError; { If not successful, then exit }
Exit; { under error condition. }
End;

If CRCMode { If we were told to support CRC }
then { then tell user we're trying to.}
Writeln('Testing for support of CRC...');

CRCOn := True; { Assume we are really doing CRC }

Count := 0; { This counts 3 second timeouts for us. Start }
{ with none while attempting to initiate CRC }

CANNum := 0; { No CAN's received yet. }

EOTNum := 0; { No EOT's received yet. }

ForceACK := False; { Don't for an ACK on any blocks yet. }

LastBlock := 0; { Didn't have a last block, so set it to zero }

BytesReceived := 0; { No bytes received yet. }

BlockNum := 0; { No blocks received yet, so set to zero. }

TotalBlocks := 0; { Ditto. }

TotalErrors := 0; { No errors yet either. }

TimeOut := True; { Prime the Repeat/Until loop }

If CRCMode { If we're supposed to do CRC, then try initiation. }
then
Repeat { Start of loop }

If TimeOut { If we had a timeout or this is start of loop, then}
then
Com_TX('C'); { send a 'C' to try to initiate CRC xfer. }

If not Com_Carrier { If we don't have a carrier for some reason, }
then
Begin { Return an error to user. }
XmodemReceive := CarrierLostError;
Close(File1);
Exit;
End;

MonitorReceive(Ch,3,TimeOut); { Monitor receive for 3 secs }

If TimeOut { If no response in 3 secs, then increment timeout ctr}
then
Inc(Count);

{ This loop continues until we've either got an SOH (start of a }
{ 128 byte block), a STX (start of 1K block), or the timeout ctr }
{ is 3 (meaning that the transmitter does not support CRC, and the}
{ xfer is to be checksum). }

Until (Count = 3) or (Ch = SOH) or (Ch = STX);


If TimeOut { If we're still sitting on a timeout, then assume a }
then { checksum. If TimeOut = False, then Count <> 3, and }
{ CRC mode was initiated from above loop. }
Begin
Writeln('Resorting to checksum now...');
CRCOn := False; { Not doing CRC here }
StartUp := False; { set the start up flag for checksum }
Com_TX(NAK); { send te first NAK to initate checksum }
End
else
Begin
StartUp := True; { If CRC is initiated, then set startup flag }
End;

NumNAKs := 0; { No NAK's for errors yet. }
Done := False; { Prime the loop, we're not done - we're beginning }
Operation := 1; { First operation we're looking at is 1 }

GetTime(Hour,Min,Sec,Hund); { Grab the starting time before we get }
StartTime := (Hour*3600) + (Min * 60) + Sec + (Hund/100); { started }

GotoXY(1,13); { Tell the user what's going on. }
Writeln('FileName : ',FileName);
Write('Error Correction : ');
If CRCOn
then
Writeln('CRC-CCITT')
else
Writeln('Checksum');
Writeln('Block Number : ',0);
Writeln('Bytes Received : ',0);
Writeln('Characters/Second : ',0);
Writeln('Elapsed Time : ',0,' min. ',0,' sec. ');
Writeln('Block Errors : ',0);
Writeln('Total Errors : ',0);


{ This loop has a very large CASE statement in it used to determine how }
{ data received in interpreted. I have divided the receiving of the }
{ Xmodem block into 5 steps (the variable Operation iterates from 1 to 5 }
{ on every block, and it's value determines which step is next taken in }
{ the loop.) The following is a list of the steps I have defined: }

{ Step 1: Receive an SOH, STX, EOT, or CAN character. SOH means that }
{ a 128 byte block is comming. STX means that a 1024 byte }
{ block is comming. EOT means that the file xfer is done. The }
{ code requires two of these to recognize a true EOT condition. }
{ A CAN character means the remote end has aborted the xfer. }
{ The code must receive two of these to recognize a true cancel }
{ condition. }

{ Step 2: Receive the block number. This is just 1 byte long. }

{ Step 3: Receive the one's complement of the block number. If the char }
{ received here is NOT the one's complement of the block number }
{ then the code simply returns to step 1, assuming the chars }
{ received thus far were garbage caused by line noise. If the }
{ byte here is the correct one's complement of the block, then }
{ it is assumed that the block has begun. If the block # does }
{ equal the next block expected in sequence, protocol aborts }
{ quickly with 2 CAN's. If the block number is the same as the }
{ last one, then a flag is set to force an ACK to be sent for }
{ this block down in step 5. }

{ Step 4: This step involves receiving the actual data of the block. }
{ If an SOH was received in step 1, then 128 bytes are expected }
{ from the transmitter. If an STX was received in step 1, then }
{ 1024 bytes are expected from transmitter. As each byte of }
{ data comes in, the CRC or Checksum (whichever is aplicable }
{ for this xfer) is updated. The data is stored in an array. }
{ When the number of bytes expected in the data has been rec'd }
{ we proceed to step 5. }

{ Step 5: Receive the block check. For CRC xfers, a 2 byte CRC is sent }
{ on the end of the block. For Checksum xfers, sum of the }
{ data bytes in the block mod 256 is sent. This step invloves }
{ receiving the CRC or checksum, and then comparing this value }
{ with the one we figured in step 4 as the data came in. If }
{ the check values are the same, or the ForceACK flag (see step }
{ 3) is True, then we send an ACK to the transmitter. If the }
{ ForceACK flag is false, we save the data to the disk file, }
{ otherwise, the data in this block is already in the file, and }
{ does not need to be saved twice in a row. If the check val }
{ does NOT equal the one we figured, then a NAK is sent, and }
{ the data is not saved to disk. In either case, we go to step }
{ 1 & continue processing. If the block is ACK'd, then we zero }
{ our NAK counter because 10 errors must happen on the same }
{ block in order to cause an error. If the block comes thru OK }
{ then we needn;t keep track of the NAKs for it anymore. }

{ Keep in mind that if at any time a span of 10 seconds occurs without }
{ getting a character from the xmitter, a timeout condition occurrs. }
{ When we have a timeout condition, we send a NAK, and increment our }
{ NAK counter. If the NAK counter ever exceeds 10, the protocol is }
{ aborted by sending two CAN's to the transmitter, and exiting the prog. }
{ Also, if ever we lose carrier, or the user presses a key, then the }
{ protocol is aborted immediately with 2 CAN's. -Andrew }


Repeat {Start a loop that only ends when Done = True }

If not Com_Carrier { If we ever lose carrier, generate error & exit }
then
Begin
XmodemReceive := CarrierLostError;
Close(File1);
Exit;
End;
{ StartUp is True if we've initiated CRC correction and the first }
{ SOH or STX character is held in variabel Ch. }
If StartUp { So, if first SOH/STX is in Ch......}
then
Begin
StartUp := False; {...then reset flag, and reset NAK Count... }
NumNAKs := 0;
End
else { ....otherwise try waiting for a character }
Begin

{ When Operation = 200, the protocol has an abort condition, and }
{ We should not wait around for any characters. }

If Operation <> 200 { If we've not got an abort condition.... }
then
Begin
Repeat {...then start a loop that continues until we've }
{ received a character or 10 timeouts have }
{ occurred on this block. }

If Com_RX_Empty { If the receive buffer is empty (i.e. }
{ there is no character waiting to be }
{ used), then start waiting...... }
then
Begin
MonitorReceive(Ch,10,TimeOut); { For for 10 secs }

If TimeOut and (EOTNum = 1) { If 10 secs pass w/o }
{ character & we've been waiting for the second }
{ EOT, then we'll issue a wairning to user and }
{ exit, assuming the 2ns EOT is not comming. }
then
Begin
XmodemReceive := EOTTimeOut;
Close(File1);
Exit;
End;
If TimeOut { If we had a timeout, but were not }
{ necessarily looking for a 2nd EOT, then we'll }
{ increment out timeout counter & send a NAK. }
then
SendNAK;
End
else { If there was a character in the buffer, by all }
{ means, let's go get it now and skip all that }
{ junk in the above Begin/End pair. }
Begin
Ch := Com_RX;
TimeOut := False;
End;

If KeyPressed { If the user presses a key, let's cancel }
then { the protocol now. }
Begin
Operation := 200;
XmodemReceive := OperatorAbortError;
TimeOut := False;
End;

Until (Not TimeOut) or (NumNAKs = 11); { This loop keeps }
{ on going until we get a character or 10 timeouts have }
{ happened. }

End;
End;

{ OK, here is where the code decides what actually gets done with each }
{ character it receives. Operation holds the step number it will do }
{ next. As each step is completed, Operation is set to the next }
{ consecutive step, so it will do the right thing when it loops thru }
{ here next time. }


Case Operation of
1 : Begin { Step 1, waiting for SOH, STX, EOT, or CAN }
Case Ch of
SOH, { If it was SOH or STX, then...}
STX : Begin
Operation := 2; { we think block has started. }
CANNum := 0; { reset a few counters & set up }
EOTNum := 0; { defaults. }
ForceACK := False;
NumNAKs := 0;
Case Ch of
SOH : MaxDataLength := 128; { SOH = 128 byte block }
STX : MaxDataLength := 1024; { STX = 1024 byte blk. }
End;{Case}
End;

EOT : Begin { If it was an EOT, then... }
CANNum := 0; { we certainly didn't get 2 of these.}
Inc(EOTNum); { but increment EOT counter. }
If EOTNum = 2 { If we got 2 EOT's together, then }
then
Begin
Com_TX(ACK); { ...ACK the 2nd EOT and exit }
{ under a normal condition. }
Writeln('End of Transmission.');
XmodemReceive := NoError;
Done := True;
End
else { But if this was first EOT, we'll NAK it }
Begin
Com_TX(NAK);
End;
End;
CAN : Begin { If char was a CAN, then }
Inc(CANNum); { Increment CAN counter }
If CANNum = 2 { If we've gotten 2, cancel w/ error }
then
Begin
Done := True;
Writeln('Xmodem Receive Cancelled.');
XmodemReceive := RemoteCancelError;
End;
{ If we've only gotten 1 CAN, ignore it right yet. }
End;

else Begin { If char wasn't SOH, STX, EOT, or CAN, then }
EOTNum := 0; { Ignore it, as it may be garbage, but }
CANNum := 0; { we'll reset our counters anyway. }
ForceACK := False;
End;
End;{Case}
End;
2 : Begin { Step 2 - Get the block number }
BlockNum := Ord(Ch); { We're assuming from the fact that an }
Operation := 3; { SOH or STX was received that the block # is }
{ next to be sent. But if garbage caused the SOH/STX, we'll }
{ catch the error in the next step. }
End;
3 : Begin { Step 3 - Get ones complement of block number }

If BlockNum = (not Ord(Ch)) { If one's compl is correct for }
{ the block # received in step 2, we're officially }
{ assuming the block has started. }
then
Begin
If (( (LastBlock + 1) and $FF ) = BlockNum) or
(LastBlock = BlockNum)
{ If the block number is the next in sequence, or it is the }
{ same one we got before, we'll accept it. }
then
Begin
ForceACK := LastBlock = BlockNum; { If last block }
{ equals this block, then ForceACK is True. }
Operation := 4; { Go to next step on next loop }
DataNum := 0; { No data received in block yet. }
CRC := 0; { Clear out the check, regardless }
Checksum := 0; { if it's CRC or checksum }
End
else
Begin { If block # is our of seq, STOP NOW! }
Operation := 200;
XmodemReceive := BlockOutOfSequenceError;
End;
End
else
Begin { If line noise caused a false SOH/STX, then the }
{ block # and the block #'s one's compl won't be }
{ correct, so if that happens, here is where we're }
{ going back to step 1 to wait for a better SOH or }
{ STX. }
Operation := 1;
End;
End;
4 : Begin { Step 4 - Receive the data block. }

Inc(DataNum); { Increment our data received counter }
Data[DataNum] := Ord(Ch); { store character in the array. }

If CRCOn
then
CRC := UpdCRC(Ord(Ch),CRC) { If CRC mode, then update CRC }
else
Checksum := Checksum + Ord(Ch); { else update checksum. }

If (DataNum = MaxDataLength) { If we've received the total amt }
{ of the data (determined in step 1 as either 128 or }
{ 1024), then it's time to go to step 5. }
then
Begin
Operation := 5;
CRCBytes := 0; { have gotten 0 bytes of CRC yet (see }
{ step 5 about this). }

ReverseWord(CRC); { We're reversing our CRC to be right }
{ for transmission. The xmitter will be sending it bit reversed. }
{ NOTE: From this point on, we don't actually have the CRC of the data }
{ in the CRC variable. We only have what we expect the xmitter to send. }

End;
End;
5 : Begin { Step 5 - get error check & verify block's correctness }

If CRCOn { If we're doing CRC, then handle 2 CRC bytes}
then
Begin
If CRCBytes = 0 { If we've not gotten anything for CRC }
{ yet, then treat this char as the high }
{ byte of the 16 bit word. }
then
Begin
BlockCRC := Ord(Ch) * 256; { Make it high byte }
Inc(CRCBytes); { increment byte counter for CRC }
End
else
Begin { If we've gotten a byte before on the CRC, }
{ this one is the lower byte. }

BlockCRC := BlockCRC + Ord(Ch); { add in low byte }

If (CRC = BlockCRC) or ForceACK { If CRC is right }
{ or we're forced to ACK this block, then we'll }
{ send an ACK....... }

then
Begin
Com_TX(ACK); { Send ACK to xmitter. }
LastBlock := BlockNum; { Keep track of the }
{ last block sent in LastBlock }

Operation := 1; { Back to waiting for SOH or}
{ STX (next block starting) }

If not ForceACK { If we were not forced to }
{ ACK this block, then we can save it to }
{ disk. }

then
Begin
{ write MaxDataLength (step 4) bytes }
{ to disk }
BlockWrite(File1,Data,MaxDataLength,Status);
{ Increment counter of bytes }
BytesReceived := BytesReceived + MaxDataLength;
{ Increment counter of blocks }
Inc(TotalBlocks);
End;
NumNAKs := 0; { Zero NAK/error counter }
UpdateVideo; { Tell user what's going on. }
End
else
{ If the CRC ** DOES NOT ** match, and we're }
{ not forcing an ACK on this block, then we'll }
{ have to NAK it because it's not good data. }
Begin

Repeat { Wait until there's not anything in }
{ the buffer. This is important to do if }
{ there was an error. Line noise might have }
{ caused a bunch of extra characters to come }
{ across, and we'll want to get rid of them. }
Ch := Com_Rx;
Until Com_RX_Empty;

SendNAK; { NAK the block, Operation is }
{ automatically set to 1, unless we've gone }
{ more than 10 errors on this block. }
End;
End;
End

{ Whew! That was a lot of stuff to do when we get to the end of the }
{ block. Fortunately, most of it (updating video, saving to disk) all }
{ happens after we've put the ACK in the buffer to be sent. Thus, the }
{ ACK is being xmitted, and the next block may already be being sent to }
{ the COM port buffer before we actually get out of here & ready to }
{ take the next block. The other steps are very quick by comparison, }
{ so we easily catch up with the buffer. On faster machines, we may }
{ find the code actually waiting for the next character to come in only }
{ a few loops later. Of course, baud rate has a lot to do with how }
{ much waiting we're going to do. }

{ Enough of the excursions...now let's see what happens when we check }
{ a block & save it under Checksum correction: }

else
Begin

If (Checksum = Ord(Ch)) or ForceACK { If the csum is }
{ correct or we're forced to ACK this block, then }
then
Begin
Com_TX(ACK); { Send the ACK }
LastBlock := BlockNum; { Keep track of blk. seq. }
Operation := 1; { we're going to step 1 again }

If not ForceACK { If we're not forced to ACK this }
{ block, then we'll save it to disk. }
then
Begin
{ Write the data block }
BlockWrite(File1,Data,MaxDataLength,Status);
{ increment bytes recevied counter }
BytesReceived := BytesReceived + MaxDataLength;
{ increment blocks counter }
Inc(TotalBlocks);
End;
NumNAKs := 0; { Zero out error counter }
UpdateVideo; { Tell user what has been happening }
End
else
Begin { If we're NAKing this block, then....}

Repeat { wait for the buffer to get empty. }
Ch := Com_Rx;
Until Com_RX_Empty;
SendNAK; { Send a NAK on the block }
End;
End;
End;

200 : Begin { Here is where it winds up if Operation = 200 }
{ This isn't actually a step. But the computer winds up here if }
{ ever we need to abort the protocol, and need to tell the xmitter }
{ this by sending two CAN's. }
Com_TX(CAN);
Com_TX(CAN);
Done := True; { time to drop out of loop }
End;
End;{Case}

Until Done; { Loop only lasts until Done }

Close(File1); { Close the file now }

End;




  3 Responses to “Category : Pascal Source Code
Archive   : XFER10.ZIP
Filename : SXRX.INC

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/