Siemens Any Pointers
Siemens Any Pointers
into a string. The task sounded simple enough. I was to receive a NULL (Hex 00) terminated block of characters through an Ethernet-I/O to DP coupler and I needed to copy these characters into a string and set the string length. I knew that if I passed pointers to the input array of characters and the output string to an STL function, the process should be fairly easy. This led me down an education path that I hadn't planned on. All of the information needed is buried in the Siemens documentation, but it was difficult for me to put it all together. One simple example could have explained the whole story and saved an entire day trial and error until I fully understood how to accomplish the task. First a short review on ANY pointers. ANY pointers are NOT pointers in the sense that they are an address that points to data as is the case in "C" or assembly language. Siemens pointers (both regular pointers and ANY pointers) are actually structures that contain a memory area identifier and an index (address) into that particular memory area. In the case of an ANY pointer, the full description is: Byte 0 1 2-3 4-5 6-9 Description 10H for Step 7 - Block Identifier Data Type Repetition Factor Block Number (in the case of a Data Block) Pointer To The Data. This is actually an index into a particular memory area
For a full description of ANY pointers, refer to the Siemens "Programming With Step-7" manual. Secondly, a short review on parameters when calling a function. When a function is called with an ANY pointer as an input, output, or InOut parameter, the ANY pointer is not actually passed to the function. The address of the ANY pointer structure is passed to the function. In a way, it is a double indirection. The function gets a pointer to the pointer that points to the data. Another way of looking at it is that the function receives a pointer to a structure that contains the ANY pointer data. In order to be able to use the ANY pointer, we must first extract the parts of the any pointer that we are interested in. In the application shown below, it is assumed that both the input and the output data would be contained in Data Blocks. Therefore, the data type is not extracted from the ANY pointer data block. However, the data block number (bytes 4-5) as well as the data index (bytes 6-9) are extracted so that the Data Blocks can be opened and the indexes can be transferred into the address registers (AR1 and AR2). Strings. Strings are arrays of characters (bytes) with the first byte being the maximum length possible for the string (the defined size when the string was declared or created). The second byte indicates the length of the current string contained in the variable. The actual data for the string starts at the third byte. For details on strings, see the "Programming With Step-7" manual. A couple of quick notes about the application below. The application only works if both the input and output data are contained in Data Blocks. If other data areas need to be supported, the application will need to be expanded. FUNCTION "CharsToS" : VOID TITLE = AUTHOR : JDK VERSION : 0.1 VAR_INPUT InChars : ANY ; END_VAR VAR_OUTPUT OutString : ANY ; END_VAR VAR_TEMP
CharsBlock : WORD ; CharsPointer : DWORD ; CharsLength : WORD ; StringBlock : WORD ; StringPointer : DWORD ; StringTotalLength : WORD ; StringLength : BYTE ; NullIndex : BYTE ; END_VAR BEGIN NETWORK TITLE = //This function will copy characters from a byte array and put them into a string. //The character array should be a NULL (Hex 00) terminated group of characters. //The function will look for the NULL and set the current string length. If the //input string does NOT contain a NULL, the function will continue to fill the //output string until the maximum string length is reached. // L P##InChars; // Get The Address Of The Any Pointer LAR1 ; // Move The Pointer To The Any Pointer Into AR1 // AR1 (Address Register 1) contains the address of the ANY pointer (AR1 Contains a pointer to the block of data // known as the ANY pointer). Extract the Repetition from the ANY pointer (in our case, number of bytes) L W [AR1,P#2.0]; // Load Repetition (Num Of Bytes In Our Case) Contained In The Any Pointer T #CharsLength; // Save Number Of Characters In Temp Word Variable // AR1 Now Contains The Address Of The Any Pointer. Extract The Block Number And Address // (Offset Pointer Into The Data Block) From The Any Pointer L W [AR1,P#4.0]; // Load Data Block Number Contained In The Any Pointer T #CharsBlock; // Save Block Number In Temp Word Variable OPN DB [#CharsBlock]; // Open The Data Block. // The Block Number Was Extracted From The Any Pointer By Loading The DB Number Into The Accumulator // And Then Storing It Into A Temporary Variable. The Data Block Was Then Opened With The Open Command // With The Temporary Variable As The DB Number. I Was Hoping To Just Open The Data Block Using The Number // In The Accumulator And Not Have To Store It In A Temporary Variable, But Never Figured Out The Syntax. L D [AR1,P#6.0]; // Extract The Offset Pointer Into The Data Block By // Loading The Value Into The Accumulator T #CharsPointer; // Save The String Pointer Into A Local Temp DWord L P##OutString; // Get The Address Of The Any Pointer LAR1 ; // Move The Pointer To The Any Pointer Into AR1 // AR1 Now Contains The Address Of The Any Pointer. Extract The Block Number And Address // (Offset Pointer Into The Data Block) From The Any Pointer L W [AR1,P#4.0]; // Load Data Block Number Contained In The Any Pointer T #StringBlock; // Save Block Number In Temp Word Variable OPN DI [#StringBlock]; // Open The Data Block. L D [AR1,P#6.0]; // Extract The Offset Pointer Into The Data Block By // Loading The Value Into The Accumulator
T #StringPointer; // Save The String Pointer Into A Local Temp DWord LAR2 ; // Transfer The String Pointer Into Address Register 2 L #CharsPointer; LAR1 ; L DIB [AR2,P#0.0]; T #StringLength; T DIB [AR2,P#1.0]; // Initialize Current String Length To Maximum Length L 0; T #NullIndex; // Initialize NullIndex To 0 +AR2 P#2.0; // Increment Address Pointer To First Byte Of String (After Max. Length and Length) Rep1: L DBB [AR1,P#0.0]; // Load Next Character From Input T DIB [AR2,P#0.0]; // Store Character In String Output L 0; // Load 0 For Comparison ==I ; // Compare Character To Null JC Exit; // Found Null, Exit Routine // We Haven't Found The Null Character Yet. Increment The Index, Check For End Of String, And Try Next Character L #NullIndex; INC 1; T #NullIndex; L #StringLength; ==I ; JC Exit; // Jump if end of string. String is at maximum length. No Nulls Found +AR1 P#1.0; // Increment Character Pointer To Next Character +AR2 P#1.0; // Increment String Pointer To Next Location In String JU Rep1;
Exit: L #StringPointer; // Load pointer to beginning of string LAR2 ; // Move pointer to address register L #NullIndex; // Load NullIndex T DIB [AR2,P#1.0]; // Store NullIndex at current length of string SET ; // Be Sure RLO Is On Prior To Exit SAVE ; // Save RLO Into BR. This Ensures ENO Will Be On For Chaining The Function In Ladder BE ; // Exit routine
END_FUNCTION