Mid-Michigan Computer Consultants, Inc.
509 Center
Bay City, Michigan

Sales (989) 892-9242             Support (989) 686-8860

ANSI Standard PL/B Language


PL/B string handling is very powerful and the techniques easy to use. As in most languages, a string is simply a series of bytes which can be treated as a whole or in part. Strings are one of the two most common data structures used in programming, the other is a numeric variable.

PL/B strings are defined mainly by their length. That's about all a programmer needs to know or be concerned with. The string is defined with the DIM statement as follows:
LAST_NAME    DIM    30
CITY         DIM    20
STATE        DIM     2
ZIP          DIM    10
Strings can also be defined dynamically at run time. There are some advantages to this but not many. For most purposes we prefer to define strings explicitely in the source code.


String variables are controlled by two pointers, each of which can be manipulated by the programmer:
  • LENGTH POINTER defines the length of the string. The interesting point is that this can be changed on-the-fly by the programmer. Normally the length cannot be extended past the originally defined length, but the length can be made shorter.

  • FORM POINTER defines the logical starting point of the string. Normally strings start with the first byte, but the programmer can easily change the form pointer to reference any byte within the string.
Most PL/B operations work on the "logical string". That means the bytes starting at the FORM POINTER position and going to the LENGTH POINTER. Manipulating these string pointers enables the programmer to easily process any sub-string within the whole.

Consider for example the following string and pointers:
NAME  INIT  "Mid-Michigan Computer Consultants"
                 ^               ^
                 FP              LP
The FORM POINTER in this example is set to 5, the length pointer is set to 21. The LOGICAL string for this variable is
       "Michigan Computer"
If you were to MOVE NAME TO WORKNAME only the sub-string would be moved.

Most instructions have a SENDING field and a RECEIVING field. In the MOVE shown above, NAME is the sending field and WORKNAME is the receiving field. In most instructions, the operation will use the LOGICAL string from the SENDING field but will start at the first byte of the receiving field. After the operation is completed, the receiving field's FORMPOINTER will be set to 1 and the LENGTH POINTER will be set to the number of bytes moved.
NAME     INIT  "Mid-Michigan Computer Consultants"
                    ^               ^
                    FP              LP
.... after the move workname will be:
              "Michigan Computer"
               ^               ^
               FP              LP		 
It's important to note that if the RECEIVING field already contains more data than what is moved, the original data is NOT erased or removed. For example:
NAME     INIT  "Mid-Michigan Computer Consultants"
                    ^               ^
                    FP              LP
WORKNAME INIT  "This is the original data in workname"
.... after the move workname will be:
              "Michigan Computernal data in workname"
               ^               ^
               FP              LP		 
In that example, the logical string which was the middle part of NAME is moved to WORKNAME starting at the first byte. The remaining data in WORKNAME is NOT changed. But the LOGICAL STRING in workname is only the portion that was moved.


There are numerous string related instructions. The ones we find most useful include these:
A very useful trick is to use UNPACK to completely clear a string and make it nul. The PL/B language reference says that "If the source string is a nul, all character string items are blank filled and cleared, and all numeric items are zeroed." That insures that the output string is nul.
NUL         INIT    1
FIELD       INIT   "This is a dummy string"
            UNPACK NUL,FIELD        //completely clears FIELD
This technique can be used to clear strings, records, lists and most other structures.

NUL Strings

NUL strings are simply string items which have no apparent contents. That is, the LENGTH POINTER is zero. When a string is created with a DIM instruction it is NUL by definition:
       NUL DIM 1

Nul strings have some important characteristiccs and useful features.
  • You cannot MOVE a nul string to another string. Nothing will happen.
             MOVE NUL to NAME
    That MOVE does nothing and the value of NAME is unchanged.

  • A string that contains any data, including one or more spaces is NOT nul.

  • A nul string can be used in an UNPACK instruction to clear everything in the receiving string.

A nul string simply has a length of ZERO. The string may physically have data within it's physical space.

This can be useful in some cases. One might have multiple text items within a single string. By manipulating the form and length pointers, the variable can appear to have different contents. (This can also be very confusing when one comes back to the program months later.)

The following example shows how a string can appear nul but still have data:
 NAME INIT     "MMCC, Inc."             //normal string with contents
      CLEAR    NAME                     //Set form and length pointers to zero
      DISPLAY  "name:":                      
               *HON,*LL,NAME,*PL,*HOFF  //shows nothing 
      RESET    NAME,3                   //point to byte 3 (extend length to match)
      DISPLAY  "name:":                      
               *HON,*LL,NAME,*PL,*HOFF  //shows "C"
      RESET    NAME                     //point to byte 1 (don't change length)
      DISPLAY  "name:":                      
               *HON,*LL,NAME,*PL,*HOFF  //shows "MMC"

from the Sunbelt PL/B Help Files:
The WHEREIS instruction scans a string for the first occurrence of another string.
WHEREISLAST scans the string in reverse order.
The instructions use the following format:
[label]  WHEREIS     {source}{sep}{search} [{sep2}{result}]
[label]  WHEREISLAST {source}{sep}{search} [{sep2}{result}]
label Optional. A Program Execution Label.
source Required. A previously defined Character String Variable, literal, equated character value, or a character value for which to search.
sep Required. A comma or one of the following prepositions: BY, TO, OF, FROM, USING, WITH, IN, or INTO.
search Required. A Character String Variable or Literal whose logical string is searched.
sep2 Optional. A comma or one of the following prepositions: BY, TO, OF, FROM, USING, WITH, IN, INTO, or GIVING.
result Optional. A Numeric Variable that receives the form pointer value in the {search} string where the {source} string was found.

Flags Affected: EOS, OVER, ZERO

Note the following:
  • The {source} and {search} variables are not changed by the operation of this instruction.

  • The EOS flag is set TRUE if the {source} or {search} is NULL.

  • The ZERO flag is set FALSE when the {source} string is found in the {search} variable. If the optional {result} variable is specified, the form pointer value of the located string is stored. The ZERO flag is set TRUE if the {source} string is not found in the {search} string.

  • WHEREIS searches in a forward order.

  • WHEREISLAST (added in runtime version 9.1.C) seaches backward.

  • The OVER flag is set TRUE if {result} is too small to receive the form pointer value.

AND	Bitwise AND two characters together.
APPEND	Append a string to another string.
BUMP	Add one to the Form Pointer.
CHOP	Move a variable to another while removing trailing spaces.
CLEAR	Set Form Pointer & Length Pointer to zero.  Moves zero to numeric field.
CMATCH	Match a character with another character.
CMOVE	Move a character.
COUNT	Count number of characters in a list of variables.
EDIT	Apply a special display format to a character variable.
ENDSET	Set the Form Pointer equal to Length Pointer.
EXTEND	Append one or more blanks to a string.
FILL	Fill character variables with a value.
FINDCHAR	Search a string for a character from a list.
LCMOVE	Move the Length Pointer character to a variable.
LENSET	Set the Length Pointer equal to Form Pointer.
LOAD	Move one of several variables to another variable.
LOWERCASE	Convert a string to lower case.
MATCH	Match a variable with another variable.
MOVEFPTR	Move the Form Pointer value to a numeric variable.
MOVELPTR	Move the Length Pointer value to a numeric variable.
MOVELS	Move a variable to another variable respecting the Logical String.
MOVEPLEN	Move the Physical Length value to a numeric variable.
MOVEPTR	Move a pointer variable to another pointer variable.
NOT	Bitwise NOT a character and move to another variable.
OR	Bitwise OR a character with another character.
PACK	Append several variables to another variable.
PACKKEY	Append several variables to another variable respecting Physical Length.
PARSE	Select next several characters based on a mask.
PARSEFNAME	Retrieve a filename from within a string.
REMOVE	Move a variable to another variable and adjust the Form Pointer of first variable.
REPLACE	Change characters to other characters in a character string.
RESET	Set the Form Pointer of a string variable.
SCAN	Search a variable for a character string.
SDELETE	Delete several characters from a character string.
SEARCH	Search a set of variables for starting with a character string.
SET	Assigns a variable or list of variables to a value of one.
SETLPTR	Set the Length Pointer of a string variable.
SFORMAT	Change the Physical Length value of a string variable.
SINSERT	Inserts a string variable into another string variable.
SMAKE	Create a string variable.
SPLICE	Replace logical contents of one variable with another.
SQUEEZE	Move a variable to another while removing characters.
STORE	Move a variable to one of several variables.
TEST	Bitwise test one character with another character.
TYPE	Determine if a string variable has a valid numeric format.
UNPACK	Split a variable to several (usually smaller) variables.
UPPERCASE	Convert a string to upper case.
XOR	Bitwise exclusive or one character with another character.


From a Jan - Feb 2010 discussion of how to reverse a string

From Gerhard Weiss - 02/03/2010
Is there a way to reverse the order of a string?

i.e. if the string was ABCDEFG

The routine would make it GFEDCBA

I am thinking of doing something like
FLD1   DIM 20
FLD2   DIM 20
       ENDSET FLD1
         CMOVE FLD1, FLD2
         BUMP  FLD1, -1
This is incomplete. I would need a counter and a few other things. I was just wondering if there was an easier way of doing it.
Actually I want to check if the end of the string is .INI.
I think this might do the trick:
FLD1   DIM 20
       ENDSET FLD1
       BUMP FLD1,-3
       MATCH ".INI",FLD1
       IF EQUAL
	     ...... do something ....

From Jim Kovalsky:
How about:
       scan    ".ini",string
       if equal
           I found it
           it's not there
       reset   string

from Don Sanford I think there were two questions.....reverse order and identify ".ini"
.   For the reverse order:
input  dim 12   ; assume large enough for data
output dim 12
xn     form 2   ; array pointer
aa     dim 1(12)

       unpack  input, aa     ;array elements up to 12
       count   xn,    input  ;number of bytes input
         append aa(xn), output  ;begins with "last" character in array 
         decr xn
       repeat until (xn=0)
       chop output
It's very early in the morning.... but I think this would work. Didn't test it.
From Gerhard
Using an array is an interesting idea. A little more optimized.
input   dim 12    ;assume large enough for data
output  dim 12
aa      dim 1(12)
        unpack input,aa     ;array elements up to 12
        pack output with aa(12),aa(11),aa(10),aa(9),aa(8):
                         aa(7), aa(6), aa(5), aa(4),aa(3):
                         aa(2), aa(1)

from David Gabler
what about: (untested code)
       MOVELPTR  #strSource,#lngSourceFptr
Now if position is 4 less than #lngsourcefptr, it was at the end of the string.

Also, I think in the following code, your pointer is initialized to whatever the input string is, so it only gets set to 300 if you don't pass anything in. In other words, I don't think your limit is really 300.
#strSource DIM ^300

from Gerhard
I like it!!!
I never used the WHEREISLAST before.
Thanks for giving me something to noodle around in my brain.
I like the idea of not changing either of the strings, which should allow for my routine to change the Source and Dest to pointers

re: #strSource DIM ^300
Before the DIM ^{size}. The size of the field was stored right in the PLC. So if you did DIM 300, 300 bytes would be stored in the PLC. Using the DIM ^300 only stores a pointer in the PLC (I am guessing one or two bytes) and at run time the field is allocated to the full 300 bytes. This DIM ^{size} decreases the PLC size DRAMATICALLY, which helps greatly when using the Application server.


Send e-mail to MMCC.

Write to MMCC Technical Support at:
MMCC, Inc.
600 W. Midland
Bay City, MI 48708
(989) 686-8860
| Home   |

© 1997 - 2003 MMCC, Inc. All Rights Reserved.
Report problems or suggestions to