Richard Lowe Jr
Richard Lowe Jr Home

Programmers Notebook

Here is a series of articles that I wrote for The DEC Professional magazine.


Programmer’s Notebook

PLANNING
FOR A SUCCESSFUL
AUTOMATION PROJECT

By Richard Lowe

Software Techniques, Inc., Cypress, California

(Published in January 1984 issue of The DEC Professional)

Mesa Consolidated Ware District serves 140,000 customers in the thriving city of Costa Mesa, near Newport Beach, California. Within the water industry, an industry traditionally slow to innovate and tangled in regulations, Mesa has always been somewhat of a leader. They were the first in the Southern California Basin to install an ozone treatment facility, allowing them to convert low-grade ground water to high-quality drinking water. In some areas of public information and legislation, Mesa’s programs are second to none.

So it was no surprise when, in 1975, Mesa put an end to a decade of mechanical billing. Although these mechanical dinosaurs (made by Burroughs and NCR) performed adequately, they were not very flexible. The reports Mesa needs in order to manage their district effectively had to be compiled manually. The district was growing, and customer billing began to take longer and longer. Mesa would have to automate, or drown in a sea of paper.

In 1976, Mesa replaced their semi-automatic billing equipment with computer technology; they went online with the Xerox service bureau. But, after using Xerox for only one year, they were still dissatisfied. The batch-mode system was inflexible, the costs were high, and Mesa was still compiling their management reports by hand.

Mesa’s First Computer

In 1978, Mesa installed their own computer. They had contacted a local Digital OEM who, after a brief analysis, sold them a PDP-11/34 with two RM02 disk drives, seven VT52 terminals, a line printer and RSTS/E.

The OEM developed custom application software for Mesa, written in BASIC-PLUS with DMS500 records management. The software was delivered without documentation of any kind, but Mesa was assured that it would follow.

Because the system seemed to meet their needs, Mesa switched over to the new system and began running customer bills. But, after a few months, mysterious "out of balance" problems arose and strange bugs appeared.

By early 1980, Mesa recognized that their system was not powerful enough. Although they had plenty of computer hardware, the system was difficult to use, data would frequently get lost, and they couldn’t find information they needed when they needed it. Attempts to fix these problems caused other problems, and Mesa’s satisfaction with the system began to spiral downward.

Finally, two years after installation, a disk crash put an end to the system. The backup procedures were not adequate to prevent the loss of huge amounts of data, including the current versions of the source programs.

A Smarter Approach

Mesa stepped back, took a deep breath, and tackled the problem again. Smarting, but smarter, Mesa realized that in order to be an asset to their business the ideal system must:

  1. Reduce Billing and Accounting Errors
  2. Improve Customer Service
  3. Hold the line Against Cost Increases
  4. Effectively Manage A Growing District

And so, with these goals clearly in mind, Mesa embarked on a two-year program which changed the face of utility water billing. The result was a combination of data processing and office automation tools designed specifically to meet the needs of water companies. The called this system WBS-11.

In mid-1981, Mesa asked Software Techniques, Inc. (STI) to work with them to develop WBS-11. They wanted the design for WBS-11 to be based on their old system because, although this system suffered from a number of problems, it provided the basic functions provided by their district.

The problem was complex:

  • Billing and accounts receivable information must be maintained on more than 140,000 customers. Bills must be sent to each of these customers on a bi-monthly basis. If the customer is delinquent, a special bill must be mailed; eventually, the water mist be shut off.

  • Each customer can have one or more meters (which measure the amount of water used) on their property. The readings from these meters must be manually or automatically read and entered into the automated system to determine the amount to be billed to the customer. In addition, the meters must be tested periodically, and repaired or replaced if necessary.

  • A backflow device is attached to each meter. This device prevents used water from flowing back into the water system. These devices must be tested periodically, and repaired or replaced if necessary.

  • The system was designed to combine online and batch technology. From 1 to 99 meter readings (known as a batch) could be entered at a time. Each batch could be listed, adjusted and modified at will, giving each user complete control over the data before it was permanently added to the system. The batch was updated to the system master files only after the user was sure the information was correct. After the weekly meter readings had been entered and updated, the bills were printed.

  • Two years of meter readings and accounts receivable history must be maintained for each meter in the district. This history is used for forecasting and to aid in tracking down complaints.

Analysis

Our first task was to determine Mesa’s automation goals. When we determined what they hoped to solve with this new system, it became clear that most of the problems revolved around the efficiency and accuracy of the old system.

We asked the users to describe each function of the system in simple terms. They were asked what tasks they perform, where the information to do the tasks comes from, what action results from these tasks, and so on. From this, a basic flowchart of the steps required to perform the functions was developed.

Each piece of information was analyzed; who needs it, what it is used for, where it comes from, what is done with it, etc. The information gained from this analysis was compiled into a specification describing how the system appears to the user. All functions, reports, screens and data bases were clearly defined in this specification.

When the specification was completed, it was presented to Mesa, where each of the users reviewed it with his or her manager. Any problems that were discovered were reported in regular review meetings, where any disagreements or concerns could be freely voiced. Following each meeting, modifications were made to the spec and it was reviewed again. This process continued until all parties were in complete agreement.

Coding

After the specification was accepted, coding began. This was scheduled in several phases to provide greater control over the project. The first phase consisted of the simple maintenance programs and their associated reports. These included:

  1. Customer Maintenance and Register.
  2. Meter Maintenance and Register.
  3. Miscellaneous Charges and Register (special charges to be billed to particular customers).
  4. Rate Table Maintenance and Register (the calculations upon which to base the billing amount).
  5. Parameter Maintenance (used to enter information about the district using the WBS-11 system).

In the next phase, the billing functions were coded. These included:

  1. Meter Reading Entry Program.
  2. Meter Reading Register.
  3. Skipped Accounts List (which lists all of the accounts not billed within a particular area and time period).
  4. Meter Reading Update (which updates all meter information entered into a batch into the master files).
  5. Billing Register.
  6. Bill Printer.
  7. Delinquent Bill Register and Printer.

Following the billing functions, the payment functions were coded. This phase consisted of:

  1. Payment entry, Register and Update Programs.
  2. Miscellaneous Credit/Debit Adjustments Entry, Register and Update Programs.
  3. Deposits Entry, Register and Update Programs.

After the payment functions were completed, miscellaneous functions were coded. These were:

  1. Closed-account Entry and Register programs.
  2. Deposit Refund Entry, Register and Update Programs.
  3. Closings Update.

Special inquiries and functions were coded last. These functions include:

  1. Customer Account Locator (allow a customer record to be looked up by customer number, meter number, customer name "sounds like" code, and street address).
  2. Account History Inquiry (display up to the last two years of accounts receivable history for a customer).
  3. Account Usage Inquiry (display up to the last two years of meter readings for a customer).
  4. Master File Purge (physically delete records which have been marked for deletion from any master file).
  5. History Unload (copy old history records from master files into temporary files and delete the old history records from the master files).
  6. Special Magtape Backup functions (as transactions are entered into the WBS-11 system, they are backed-up into magtape. These transactions may be restored later if the system suffers a catastrophic failure).

During the final phases of coding, the documentation was completed. Three documents were delivered with the system:

  1. User’s Guide

    This document tells the user how to operate all functions of the system except those functions reserved for use by the system manager. Each step required to perform each function is discussed in the order it must be performed.

  2. Manager’s Guide

    This document describes the data structures required by the system, the menu system and any functions reserved to the system manager.

  3. Technical Manual

    This document describes how to build the WBS-11 system and the design philosophy. It is designed for use by a programmer who needs to add new functions to the system.

Training

The initial training sessions were designed to dispel the myths about computers - what they can and cannot do. We described the various components of the computer system and how they interact, giving the users a better feel for the tool they were being asked to use.

Later, the relationships between the various data structures were discussed so that the users understood exactly what occurred before, during and after something was processed. Both the automated functions and the manual procedures necessary to support them were described fully.

Last, a few of the key users were taught to use the more sophisticated tools (like DATATRIEVE).

Conclusions

As we have repeatedly discovered, the key to a successful automation project is planning. Due to the large amount of planning that went into the WBS-11 project, we were able to set Mesa’s expectations to a reasonable level, and then to meet those expectations.


Programmer’s Notebook

GETTING THE MOST
FROM BASIC VERSION 2

PART 1 - DECLARATIONS

By Richard Lowe

Software Techniques, Inc., Cypress, California

(Published in July 1984 issue of The DEC Professional)

Editor’s Note: In this issue of Programmer’s Notebook, we begin a series on BASIC Version 2. Designed to aid new BASIC V2 programmers, our series will describe new features and offer some insight (and tricks) into using them. Generally, Mr. Lowe describes the using of BASIC on RSTS, but most of the concepts may be applied easily to other operating systems.

The first installment discusses data types, data declarations and defining the program environment.

When DEC announced BASIC Version 2 at the Fall 1982 DECUS Symposium, many features were described which (finally) made BASIC a truly structured language for professional programmers. Although previous versions of BASIC were often used in business applications, programmers were forced to use sloppy practices due to the lack of structure and definition. Only with the advent of Version 2.0 was DEC able to offer professional programming as a viable language for PDP/11 and VMS applications. As a result, BASIC Version 2 is gaining popularity among serious programmers who are learning how powerful a tool BASIC can be.

All data in a BASIC program has a specific data-type that determines how many bits of storage should be considered as a unit and how the unit is interpreted and manipulated. BASIC Version 2 recognizes five primary data-types: integer, floating point, character string, RFA (Record File Address) and packed decimal (VAX-11 only). These types correspond to the BASIC data-type keywords:

  • STRING
  • INTEGER
  • REAL
  • RFA
  • DECIMAL

STRINGs

Character data is stored as a string of bytes containing ASCII codes as binary data. The first character in the string is stored in the first byte, the second character in the second byte and so on. A string data element may contain 32767 characters (65535 characters in VAX-11 BASIC.)

INTEGERs

Integer data is stored as a binary value in a byte, word or longword. Operations using bytes and words are extremely fast, since they can be performed using a single PDP-11 instruction. Longwords would be used sparingly on the PDP-11, because several instructions must be executed for each operation performed. On the VAX-11, all integer operations (bye, word and longword) are performed using a single instruction, and so, are equally efficient.

The three types of integer data are:

  1. BYTE - Each byte requires eight bits of storage. Allowable values range from -128 to 127. This data-type is useful for storing logical (TRUE or FALSE) flags, keyboard numbers, job numbers, loop counters, etc.

  2. WORD - Each word requires 16 bits of storage. Allowable values range from -32,768 to 32,767. This data-type is generally suitable for most integer operations and calculations.

  3. LONG - Each longword requires 32 bits of storage. Allowable values range from -2,147,483,648 to 2,147,483,647. This data-type is most useful for extremely large integer calculations.

REALs

Floating-point values are stored using a signed exponent and a binary fraction. If your PDP-11 does not have a floating point processor, then using real numbers will significantly slow down an application.

The four types of REAL data are:

  1. SINGLE - Each single-precision number requires 32 bits of storage. Allowable values range from .29 * (10(-38) to 1.7 * 10(38). Six digits of precision (to the right of the decimal point) are retained.

  2. DOUBLE - Each double-precision number requires 64 bits of storage. Allowable values range from .29 10(-38) to 1.7 * 10(38). Sixteen bits of precision are retained.

  3. GFLOAT (VAX-11 only) - Each GFLOAT number requires 64 bits of storage. Allowable values range from .56 * 10(-308) to .9 * 10(308). Fifteen digits of precision are retained/

  4. HFLOAT (VAX-11 only) - Each HFLOAT number requires 128 bits of storage. Allowable values range from .84 * 10(-4932) to .597 * 10(4932). HFLOAT retains 33 digits of precision.

RFAs

Record File Address values are stored as six bytes of binary data. Each Record File Address contains a block number within a file and an offset into that block. An RFA uniquely identifies a record in a file and allows the programmer to store and manipulate record addresses from RMS files.

Accessing records by RFA is much faster than other forms of random record access (the specific applications of the RFA data-type will be discussed in a future article).

DECIMALs (VAX-11 only)

A DECIMAL number contains a specified number of digits and decimal point position. The number of digits and decimal point position are specified when the DECIMAL variable or constant is DECLAREd. Each decimal value requires from zero to 16 bytes of storage. Allowable values range from 1 * 10(-31) to 1 * 10(31). This data format is useful for storing values such as dollar amounts.

EXPLICIT DATA DECLARATION

One of the most useful features of BASIC Version 2 is the ability to explicitly declare variables and constants. By forcing all variables to be explicitly declared before they are used, the command (and often difficult to detect) problem of misspelled variable names can be eliminated.

The DECLARE statement allows each variable within a module to have a different data-type. For example, all integer data does not have to be of the same type, and single and double data-types may be combined within the same module.

The DECLARE statement is used to assign a data-type to a variable, constant or function. When explicit data typing is in effect, a variable must be declared before it is used within a program module.

In the example below, the variable STAT has been declared to be an integer of the default size (BYTE, WORD or LONG). The default size can be specified in either the OPTION statement, a COMPILE time qualifier or a SET qualifier. (Note that the size specified in the OPTION statement takes precedence over the sizes specified in either the SET or the COMPILE commands.)

DECLARE INTEGER &
	STAT	! Status variable returned &
		! by various subroutine calls &

The DECLARE CONSTANT statement is used to assign a name to a constant that is internal to a module. The constant can then be referred to by name throughout the module. This feature is useful because (1) logically named constants can make a program easier to understand; and (2) since the constant is only declared once within the module, the value may be modified with only a single change to the source code.

The declaration of a string constant is shown below. Note the use of the literal constant "155"C, which is equivalent to CHR$(155).

DECLARE STRING CONSTANT &
	ANSI_MODE = “155”C + “<“ &
		! Set terminal to ANSI mode

DEFINING THE ENVIRONMENT

One of the most notable features of BASIC Version 2 is the OPTION statement. The OPTION statement allows you to set compilation qualifiers such as the default data-type, the default sizes of integer and real values (variables and constants), and the scale factor for each program module.

The TYPE qualifier sets the default data-type for variables not explicitly defined within a program module. The default data-type may be set to either REAL, INTEGER, DECIMAL (on VAX-11 only) or EXPLICIT. If EXPLICIT is specified, then an error will be generated when a variable has not been declared.

The SIZE clause sets the default data subtypes for floating-point, integer and (VAX-11 only) packed decimal format. Using the SIZE clause, the default size for INTEGER may be set to byte, word or longword, and REAL data may be set to either single or double precision. On the VAX-11, this clause may be used to set the default number of digits and decimal points for DECIMAL data.

The SCALE clause allows you to control accumulated round-off errors. REAL data is multiplied by 10 raised to the scale factor. The scale factor may be any value between 0 and 6, inclusive.

The example below shows a common use of the OPTION statement: (1) force all variables to be declared; (2) default all integer values in which the size has not been specified to BYTEs; and (3) default all real values in which the size has not been specified to DOUBLEs.

OPTION 	TYPE = EXPLICIT &
		SIZE - &
			(INTEGER BYTE &
			,REAL DOUBLE)

DEFINING THE ENVIRONMENT

BASIC Version 2 has a large variety of new features which make the programmer’s job much easier. The many data-types available let the programmer choose exactly the right one required to suit the application. Explicit data typing can reduce the number of coding errors (such as misspelled variable names), The OPTION statement allows compilation qualifiers to be specified within the source of each module.

In Part 2 of this series we’ll be building upon these concepts and discussing external subroutines, functions and constants.


Programmer’s Notebook

GETTING THE MOST
FROM BASIC VERSION 2

PART 2 - PROGRAM SEGMENTATION

By Richard Lowe

Software Techniques, Inc., Cypress, California

(Published in September 1984 issue of The DEC Professional)

This article is the second in a series about BASIC Version 2. The purpose of this series is to assist BASIC Programmers by describing new features and offering tricks and insights into taking advantage of their use. While these articles describe the use of BASIC on RSTS, most of the concepts can easily be applied to other operating systems.

A subprogram (or module) is a block of code which is created and compiled separately and then called from another program module. Each module interfaces to the rest of the program through global data structures (files, MAPs, and COMMONs) and parameters which are included as part of the call to the subprogram. Variables which are local to a subprogram cannot be accessed by any other module.

Subprograms are commonly used to break apart a large program into smaller, more manageable sections. This has several advantages. First, since the interface between subprograms is strictly defined, it is more difficult to cause unexpected side effects in other parts of the program - changes to local variables have no effect outside the module where they have been declared.

Second, since a modular program contains several small subprograms instead of one large program, compilation time can be significantly reduced. While it generally takes longer to compile all of the parts of a modular program, the entire program does not need to be rebuilt if a change is made to a single module. Only the module which has been changed needs to be recompiled.

Third, functions specific to an operating system can be isolated in subprograms. If the application is moved to a different operating system (or the functionality of the current operating system changes), then only the system-dependent subprograms need to be modified.

Finally, many programs must be split up into subprograms simply due to the memory restrictions of RSTS (or the workfile size restrictions of the BASIC compiler). Since the placement of subprograms within the program is controlled at task build-time, it is possible to reduce memory requirements through the use of overlays, resident libraries and co-trees. (Task-building BASIC Version 2 programs will be discussed in a later article on this series).

Of course, splitting up a program into multiple modules has some disadvantages. It can be more difficult to maintain, especially if there is a large number of subprograms because: (1) each module must be coded, documented and compiled separately; (2) the interfaces between the various subprograms must be carefully designed so that all of the modules in a program will work together; and (3) it may become necessary to maintain a more complicated task-building overlay file to put the entire program together.

It is extremely important to document the subprogram interfaces completely. Dozens or even hundreds of modules may depend on the variables contained within global data structures. A seemingly small change to a MAP or COMMON by an unknowing (but well intentioned) programmer can cause problems which are extremely difficult to trace.

The two types of subprograms, SUBs and FUNCTIONs, are described in detail below.

SUB SUBPROGRAMS

A SUB subprogram is a module which is accessed via the CALL statement. Optionally, parameters may be passed to the subprogram. For example:

CALL SUBPRG( P1 )

passes the variable P1 to the subprogram SUBPRG. The parameters included in the CALL statement must match the parameters defined by the subprogram exactly; the same number of parameters and the same datatypes must be passed. If this is not true, then unexpected run-time errors may result.

The existence of a SUB subprogram may be declared through the use of the EXTERNAL SUB statement. Although the EXTERNAL SUB is not required (the CALL does an implicit declaration), this statement is usually desirable because the datatype for each parameter may be declared. BASIC will print an error message if the parameters and datatypes declared in the EXTERNAL SUB do not match those of the corresponding CALL within the module.

The program fragment in Example A demonstrates the use of the EXTERNAL SUB statement to explicitly declare an external routine called SETPRI. In addition, the CALL to SETPRI is show. Note how the returned status variable (STAT) has been declared as a LONG integer, and the error text is returned by an external function. This will ease the conversion process when the program is transported to VMS because the VMS operating system returns longword status codes.

The SUB statement is used to define the beginning of a BASIC SUB subprogram. It must be the first number line of the subprogram. If parameters are passed to the subprogram, they are specified as part of the SUB statement. For example:

SUB SUBPRG( STRING X2 )

declares the start of the subprogram SUBPRG, which has a single string parameter called X2.

The last statement within a subprogram must be END SUB. When this statement is executed, control will be transferred to the statement immediately following the statement which called the subprogram.

The subprogram may also be exited using the EXIT SUB statement. When this statement is executed, control transfers to the END SUB statement.

Example B demonstrates how a SUB subprogram can be used to "hide" system specific codes from an application. If the RSTS subroutine was transported to VMS, only the subroutine would need to be modified.

FUNCTION SUBPROGRAMS

A FUNCTION subprogram is basically the same as a SUB subprogram, except that a value is returned in the subprogram name. You invoke a function in the same manner that you would one of the build-in BASIC functions. For example:

PRINT DAT$CV( INTERNAL_DATE)

would print the results of DAT$CV to the terminal.

Parameters are passed to FUNCTION subprograms in exactly the same manner as in SUB subprograms. Of course, the parameters in the function invocation must match the parameters defined by the function exactly - both in number and data-type.

The existence of a FUNCTION subprogram must be declared through the use of the EXTERNAL FUNCTION statement. The data-type for each parameter may optionally be declared at this time. This is desirable because BASIC will print an error message if the parameters and data-types declared in the EXTERNAL FUNCTION do not match those of each corresponding function invocation within the module.

Example C shows a common use of FUNCTION subprograms to perform data conversion. In this case, the FUNCTION subprogram is used to convert RSTS integer PPNs to a format suitable for printing to the user.

The beginning of a FUNCTION subprogram is defined using the FUNCTION statement. As with the SUB statement, it must be the first statement on the first numbered line of the subprogram. Parameters (if they are passed to the subprogram) are specified as part of the FUNCTION statement. For example:

FUNCTION STRING DAT$CV ( WORD DATE_INTEGER )

declares the start of the subprogram DAT$CV, which has a single string parameter called DATE_INTEGER.

To return a value within the function name, simply equate the function name to the desired value. For example:

DAT$CV = STRING_DATE

will, when executed within DAT$CV, cause the string STRING_DATE to be returned to the main program when execution of the FUNCTION subprogram is complete.

Each FUNCTION subprogram must be terminated by the END FUNCTION statement, which must be the last statement within the subprogram. When END FUNCTION is executed: (1) a value is returned to the calling program; and (2) control is transferred to the statement which called the FUNCTION subprogram.

The EXIT FUNCTION statement may be used to exit the subprogram. This statement causes control to be transferred to the END FUNCTION statement.

Example D shows the use of a FUNCTION subprogram to convert data from RSTS internal data format to a string suitable for printing.

EXTERNAL CONSTANTS

The EXTERNAL CONSTANT statement is used to declare constants which are defined outside of the module. These are commonly used to access error values, RMS codes, etc.

Example E demonstrates a common use of external constants. The mnemonic for the RSTS error "?End of file on device" has been declared as an external constant. Throughout the module, it is then referred to bu its mnemonic.

CONCLUSION

Breaking up a large program into several smaller, more manageable modules can: (1) reduce overall compilation time; (2) allow larger programs; (3) increase transportability; and (4) decrease the number of programming errors by strictly defining the interface between these subprogram segments. The interfaces between these subprograms must be well designed and documented so changes can be easily made when required. Two types of subprograms are provided: SUBs and FUNCTIONs.

In Part 3 of this series we’ll be discussing the use of MAPs, COMMONs and dynamic MAPs.


Programmer’s Notebook

GETTING THE MOST
FROM BASIC VERSION 2

PART 3 - STATIC STORAGE

By Richard Lowe

Software Techniques, Inc., Cypress, California

(Published in November 1984 issue of The DEC Professional)

This article, the third in a series, is meant to assist BASIC programmers by describing new features and offering tricks and insights into taking advantage of their use. While these articles describe the user of BASIC on RSTS, most of the concepts can easily be applied to other operating systems.

In Part 2, we discussed program segmentation. This article builds on that concept by describing a technique that is commonly used to communicate with subprograms: placing variables in static storage areas.

Variables and arrays which are placed in static storage areas are fixed in position and size (with the exception of variables in dynamic MAPs, discussed later in this article). Because the locations of these variables do not change from module to module, they can be accessed anywhere in the program. BASIC provides two statements to declare static storage: COMMON and MAP.

Whenever a MAP or COMMON statement is used, a named storage area known as a program section (or PSECT) is created. The PSECT name is the same as the MAP or COMMON name.

COMMON STATEMENTS

The COMMON statement is generally used for intermodule communications, because the values within COMMONs of the same name may be accessed by any modules within a program. This is often a convenient method for passing parameters to subprograms.

An example of the COMMON statement is show below.

COMMON 	(COMDAT)	! Define data common to all	&
		! modules in package	&
	BYTE COMDAT_RPT_OPEN	&
		! Indicates whether report file	&
		! is open (0=closed, -1=open)	&
		!	&
	,STRING COMDAT_RPT_FILE_NAME = 24	&
		! Define the name of the report	&
		! file.

If the COMMON name (in the example above, COMDAT) is not included, then BASIC defaults the name to $BLANK on VAX/VMS or .$$$$. on PDP-11 systems.

When in a particular program module, multiple occurrences of a COMMON with the same name are concatenated. For example:

100 COMMON (X) STRING T=32
110 COMMON (X) STRING Y=16

produces a string 48-byte long PSECT. The string T begins at the start of the COMMON X and is 32 bytes long. The string Y begins immediately following the end of the string T and is 16 bytes long.

MAP STATEMENTS

The MAP statement is generally associated with a file and used to create the file’s input/output buffer. The MAP statement can also be used for inter-module communications, in the same manner as the COMMON statement (as described in the section above).

A sample MAP statement is shown below:

MAP 	(PRODCT)	! Define format of product 	&
		! record.	&
	STRING PRODCT_CODE = 8	&
		! Product code	&
	,STRING PRODCT_DESC = 50	&
		! Product description	&
	,DOUBLE PRODCT_PRICE	!
		! Product price	&

MAP	(PRODCT)	! Breakdown of  “Product code”	&
	STRING PRODCT_LINE = 1	&
	,STRING PRODCT_NUMB = 4	&
	,STRING FILL = 1	&
	,STRING PRODCT_CATG = 1	&
	,STRING PRODCT_MED = 1	&

To associate a MAP with a file, the MAP clause of the OPEN statement is used. For example, the OPEN statement sh own below will open the file "DDU:CUST.IDX" and cause the MAP CUST to be associated with it. Later, when a record is retrieved from the file, its contents will be placed into the CUST MAP. The variables in the MAP can then be used to access the record.

OPEN “DDU:CUST.IDX” FOR INPUT AS FILE #Chancust	&
	ORGANIZATION INDEXED FIXED &
	,ACCESS MODIFY	&
	,ALLOW MODIFY	&
	,MAP CUST	&
	,PRIMARY KEY Custnumber NODUPLICATES

Unlike the COMMON statement, multiple occurrences of a MAP in the same module with the same name are overlayed. For example:

100 MAP (X) STRING T=32

110 MAP (X) STRING Y=16

causes a 32-byte long PSECT to be created. The string T begins at the start of the MAP X and is 32 bytes long. The string Y also begins at the start of MAP X and is 16 bytes long.

Overlaying MAPs is a useful technique for manipulating strings and accessing a file I/O buffer in different ways. For example, the name in the MAP shown below is a combination of a first and last name. This allows the program to access both strings simultaneously (by accessing NAMES_WHOLE_NAME), as well as individually (by accessing wither NAMES_FIRST or NAMES_LAST).

MAP (NAMES) &
	STRING NAMES_FIRST = 16	&
	,STRING NAMES_LAST = 16	&

MAP (NAMES) &
	STRING NAMES_WHOLE_NAME = 32

DYNAMIC MAPS

A dynamic MAP allows you to redefine the position of variables within a MAP at run time. In addition, the length of string variables may be changed.

To use dynamic maps, three statements are required: (1) the MAP statement, which defines the total size of the PSECT as well as those variables which are not dynamic; (2) the MAP DYNAMIC statement, which names the variables whose positions (and lengths in the case of strings) may change at run time; and (3) the REMAP statement, which changes the positions of dynamic variables.

A sample MAP DYNAMIC is shown below:

MAP (SAMPLE)	! Pre-allocate record buffer	&
	WORD SAMPLE_STATUS			&
		! Record status variable	&
	,STRING SAMPLE_DYNAMIC = 100		&
		! 10 names per record		&

MAP DYNAMIC (SAMPLE)				&
		! Now define the dynamic vars	&
	 STRING SAMPLE_NAME			&
		! Up to 10 names in record	&

Later, the REMAP statement is used to access the desired name:

REMAP (SAMPLE)	! Access the name		&
	WORD FILL				&
		! Skip status field		&
	,STRING FILL = 10 * DESIRED_NAME	&
		! Position to desired name	&
	,STRING SAMPLE_NAME = 10		&

The sample program demonstrates the use of dynamic maps to access a file I/O buffer. The record contains to static variables (CUST_CONTACT_NAME and CUST_CONTACT_PHONE). The REMAP is used to access the desired customer contact name and phone number from the dynamic area of the MAP.

CONCLUSION

Static storage is useful for module intercommunication and accessing record buffers. Two statements are provided to define static storage: MAP and COMMON. Dynamic maps allow MAPped variables to be repositioned within their MAPs at run-time.

Next issue, we will be discussing the new statements which (finally!) allow you to create structured code. When used correctly, these structured statements will allow you to create code that is efficient and maintainable.


Programmer’s Notebook

GETTING THE MOST
FROM BASIC VERSION 2

PART 4 - The New Structured Statements

By Richard Lowe

Software Techniques, Inc., Cypress, California

(Published in January 1985 issue of The DEC Professional)

Editors Note: This series of articles is meant to assist the BASIC programmer by describing new features. While references are to the use of BASIC on RSTS, most concepts are easily applied to other operating systems. part 3 of this series appeared in THE DEC PROFESSIONAL, November 1984.

In previous articles in this series we discussed data declarations, program segmentation and static storage. This article introduces two new structured coding statements: the SELECT/CASE statement and the IF/THEN/ELSE statement.

SELECT/CASE

SELECT is a new statement in version 2 of BASIC. It allows alternative blocks of code to be executed based upon the value of an expression. Although the same functions could be performed in previous versions of BASIC through the use of the ON GOSUB, ON GOTO and I/THEN/ELSE statements, the SELECT statement is much easier to understand.

The SELECT statement has the following format:

PRINTOUT A

SELECT	expression1
CASE	expression2
		.
		. Code
		.
CASE	ELSE
		.
		. Code
		.
	END SELECT

The keyword "SELECT" begins the select block and the keyword END SELECT ends the selection block. Each CASE keyword within the select block establishes a case block. The next CASE or END SELECT keyword ends the previous case block. When the value of expression1 matches the value of expression2, then that case block will be executed.

If the SELECT expression does not match any CASE, the code specified after the CASE ELSE keyword will be executed (if no CASE ELSE keyword is specified, then control passes to the END SELECT). In any event, when execution of the case block is completed, control passes to the END SELECT keyword.

For example, the SELECT statement show is used to execute various subroutines based upon a value input from the user. Based upon the value entered by the user (OPTION) one of the following actions is performed:

PRINTOUT B

1000	INPUT #”1”B, “Enter desired option “; OPTION
	SELECT OPTION
	CASE > “8”W, < “0”W
		PRINT #0%, “?Invalid option”
	CASE = “0”W
		GOTO 32767
	CASE “1”W TO “5”W
		GOSUB 5050
	CASE  = “6”W
		GOSUB 5060
	CASE = “7”W
		GOSUB 5070
	CASE = “8”W
		GOSUB 5080
	END SELECT
	GOTO 1000

1. If OPTION is greater than 8 or less than 0, the message "?invalid option" is printed.

2. If OPTION is between 1 and 5 (inclusive) then the GOSUB 5050 statement is executed.

3. If OPTION is equal to 6, then the GOSUB 5060 statement is executed.

4. If OPTION is equal to 7, then the GOSUB 5070 statement is executed.

5. If OPTION is equal to 8, then the GOSUB 5080 statement is executed.

Control then passes to the code following the END SELECT keyword. (Printout B).

Strings may also be used, as shown in Printout C.

PRINTOUT C

	ERROR_FLAG = FALSE
BEGIN_CHECK:
	IF 	BOUNDRY > “7”W
	THEN 	PRINT “?BUG - Array boundry too high”
		ERROR_FLAG = TRUE
	ELSE	IF	BOUNDRY <= “0”B
		THEN	PRINT “?BUG - Array boundry  + &
				“ too low”
			ERROR_FLAG = TRUE
		ELSE	HELP_FLAG = FALSE
			GOSUB SET_HELP
			EXIT BEGIN_CHECK &
				IF ERROR_FLAG
			GOSUB SET_ARRAY
		END IF
	END IF
	EXIT SUB IF ERROR_FLAG

IF/THEN/ELSE

Although the IF/THEN/ELSE statement has existed in all versions of BASIC, some new extensions have been added which make it more useful and easier to maintain. These include the END IF keyword and the EXIT keyword.

The IF/THEN/ELSE statement is used to test the value of an expression. If the expression is true, the statements following the THEN clause are executed; if the expression is false, the statements following the ELSE clause are executed (if there is no ELSE, control passes to the statement following the END IF or to the next line number).

The END IF keyword is used to terminate the most recent IF statement. This eliminates the need to terminate IF statements with line numbers. The END IF statement makes programs more readable. It can also reduce the need for the awkward logic constructs which were previously required because the only way to terminate an IF statement was with a line number.

The EXIT statement is used to pass control to the statements following an IF/THEN/ELSE block. This keyword takes the form:

EXIT Label

Label is the name of the label marking the start of the IF statement to exit.

This statement reduces the need for the GOTO statement. Program maintainability is increased because the programmer can add new code into the IF construct without being concerned about line number placement. The program is made more self-documenting because the statement immediately makes clear what is happening - an IF is being exited.

Both the END IF and the EXIT statements are shown in the following example. Two IF statements are used to verify that an array offset is within range. If this offset is valid, a subroutine (SET_HELP) is executed. If the subroutine fails (the error flag ERROR_FLAG is set), the EXIT statement is used to exit the IF. Otherwise, the subroutine SET_ARRAY is executed. Finally, two END IF statements are used to terminate the previous IFs. See Printout D.

PRINTOUT D

	ERROR_FLAG = FALSE
BEGIN_CHECK:
	IF 	BOUNDRY > “7”W
	THEN 	PRINT “?BUG - Array boundry too high”
		ERROR_FLAG = TRUE
	ELSE	IF	BOUNDRY <= “0”B
		THEN	PRINT “?BUG - Array boundry  + &
				“ too low”
			ERROR_FLAG = TRUE
		ELSE	HELP_FLAG = FALSE
			GOSUB SET_HELP
			EXIT BEGIN_CHECK &
				IF ERROR_FLAG
			GOSUB SET_ARRAY
		END IF
	END IF
	EXIT SUB IF ERROR_FLAG

CONCLUSIONS

Using the new structured programming statements allows more maintainable and understandable programs to be written. The SELECT/CASE statement simplifies programs by reducing the need for ON GOSUB, ON GOTO and IF/THEN/ELSE statements. The extensions to the IF/THEN/ELSE statement (the END IF and EXIT) increase the maintainability of programs by reducing the need for GOTOs and increasing program readability.

The next article in this series will complete the discussion of the structured coding statements by describing the various looping constructs.

Richard Lowe is head of plant relations, client relations with Software Techniques, a Cypress, California based software development company.


Programmer’s Notebook

GETTING THE MOST
FROM BASIC VERSION 2

PART 5 - The New Structured Statements, Continued

By Richard Lowe

Software Techniques, Inc., Cypress, California

(Published in February 1985 issue of The DEC Professional)

A loop is a powerful programming construct which gives programmers the capability to repeat the execution of a series of program statements. The loop control statements provided by BASIC-PLUS-2 are particularly powerful. They are very easy to use, understand and maintain.

The three types of loops which a BASIC-PLUS-2 application may use are:

• FOR/NEXT

• UNTIL/NEXT

• WHILE/NEXT

Each of these type of loops has different characteristics and purposes. The FOR/NEXT is useful when a series of statements must be executed a specific number of times. The WHILE/NEXT and UNTIL/NEXT are more useful when the loop must be executed an indefinite number of times.

FOR/NEXT LOOPS

A FOR/NEXT loop allows a series of statements to be executed a specific number of times. A loop control variable is used to determine the number of times the loop is executed. The starting and ending values of the loop control variable are determined before execution of the loop begins. An example of a typical FOR/NEXT loop is shown below:

100	FOR LOOP_CTR = “1”W TO “20”W
		TEMP_1 = ARRAY_1(LOOP_CTR)
		ARRAY_1(LOOP_CTR) = ARRAY_2(LOOP_CTR)
		ARRAY_2(LOOP_CTR) = TEMP_1
	NEXT LOOP_CTR

In this example, the three lines between the FOR and the NEXT statements will execute exactly twenty times. The first time the loop is executed, the variable LOOP_CTR will be equal to one, the second time it will be equal to two and so on, until it is equal to twenty. When LOOP_CTR is equal to 21, control will be transferred to the statement following the NEXT. LOOP_CTR will then be set back to its previous value (in this case, twenty). Thus, the value of the loop control variable will always be the last used in the loop, not the value which caused termination.

A step can also be specified, as shown below:

FOR LOOP_CTR = "1"W TO "20"W STEP "10"W

This line will cause the loop to be executed twice. First, LOOP_CTR will be equal to 1, then 11 and then 21 - which will cause the loop to terminate and transfer control to the statement following the NEXT. LOOP_CTR will then be set back to 11.

If the step value is negative, then the starting value must be greater than the ending value in order for the loop to be executed. For example:

FOR LOOP_CTR = "20"W TO "1"W STEP -"1"W

will cause a loop to be executed 20 times.

LOOP_CTR will initially be equal to 20, then 19, then 18 and so on, until it reaches 1. When LOOP_CTR is equal to zero, the loop is exited. On completion, LOOP_CTR is set back to the value previous to that which caused termination, in this case, one.

The ITERATE and EXIT statements are used to control the flow of control within a loop, for example:

	VALID = “0”W
COPY_LOOP:
	FOR LOOP_CTR = “1”W TO “20”W
		ITERATE COPY_LOOP &
			IF ARRAY(LOOP_CTR) = “0”W
		EXIT COPY_LOOP &
			IF ARRAY(LOOP_CTR) = -”1”W
		TOKEN(LOOP_CTR) = SAVE_TOKEN(VALID)
		VALID - VALID + “1”W
	NEXT LOOP_CTR

This example copies only those items from the array TOKEN which have not been marked as invalid (the corresponding element in ARRAY is equal to zero). The ITERATE statement is used to transfer control to the NEXT statement, this neatly skipping the statements which copy the element from TOKEN. In addition, the end of the array is marked with a -1. The EXIT statement is used to transfer control out of the loop when the -1 is reached.

WHILE/NEXT LOOPS

A WHILE/NEXT loops allows the execution of a series of statements while a condition is TRUE. An example of a WHILE/NEXT statement is shown below:

	ERR_STATUS = “0”W
	NUM_CST = “0”W
WHILE_LOOP:
	WHILE ERR_STATUS = “0”W
		CALL CST(ERR_STATUS, DEL_FLAG)
		ITERATE WHILE_LOOP &
			IF DEL_FLAG
		NUM_CST = NUM_CST + “1”W
	NEXT

In the example above, the statements between the WHILE and NEXT statements are executed until the CST subprogram returns an error.

Unlike a FOR/NEXT Loop, the WHILE/NEXT does not have a loop control variable. In order to exit the WHILE/NEXT loop, the program must either:

1. change a variable which was specified in the WHILE statement (ERR_STATUS in the example above) such that the expression becomes FALSE, or

2. Transfer control out of the loop.

Note that the EXIT and ITERATE commands can be used within a WHILE Loop exactly as with a FOR/NEXT loop.

UNTIL/NEXT LOOPS

An UNTIL/NEXT statement allows a series of statements to be repeated until an expression is TRUE, for example:

	TOKEN_LIST = “”
UNTIL_LOOP:
	UNTIL TOKEN = “EXIT”
		INPUT “Command”; TOKEN
		TOKEN = EDIT$(TOKEN,-”1”W)
		TOKEN_LIST = TOKEN_LIST + “/” + TOKEN
	NEXT

Here, the user is required to input a series of keywords. These keywords are appended together until the user completes the list by entering EXIT. Note that EXIT is also appended to the list of keywords.

As with the WHILE/NEXT statement, the UNTIL/NEXT does not have a loop control variable. To exit the loop, the program must either change a variable in the expression so that it becomes TRUE, or transfer control out of the loop.

Of course, the EXIT and ITERATE commands can be used within the UNTIL/NEXT loop exactly as with the FOR/NEXT and WHILE/NEXT loops.

NESTED LOOPS

Loops may be nested, as shown in the example below:

WHILE_LOOP:
	WHILE -”1”W
		FOR LOOP_CTR = “1”W TO “10”W
			MUX(LOOP_CTR) = “0”W
		NEXT LOOP_CTR
		CALL PUTMUX &
			(ERR_STATUS,END_FLAT, MUX() )
		EXIT WHILE_LOOP &
			IF ERR_STATUS <> “0”W &
			OR END_FLAG = TRUE
	NEXT

In this example, nested FOR/NEXT loops are used to initialize the elements of some two-dimensional arrays. The inner loop (FOR Y_POS …) is repeated 11 times for each iteration of the outer loop (FOR X_POS).

Different kinds of loops may be nested, as shown below:

WHILE_LOOP:
	WHILE -”1”W
		FOR LOOP_CTR = “1”W TO “10”W
			MUX(LOOP_CTR) = “0”W
		NEXT LOOP_CTR
		CALL PUTMUX &
			(ERR_STATUS,END_FLAT, MUX() )
		EXIT WHILE_LOOP &
			IF ERR_STATUS <> “0”W &
			OR END_FLAG = TRUE
	NEXT

In this example, the elements of a one dimensional array were initialized and then passed to another routine for processing. This process would continue indefinitely until the PUTMUX routine either indicates it has completed processing (by setting END_FLAG to TRUE) or it returns an error condition. Notice how the WHILE -"1"W statement was used to create a loop which would execute indefinitely.

The looping constructs provided by BASIC-PLUS-2 allow programs to conveniently repeat the execution of blocks of code. Three constructs are provided: The FOR/NEXT, which allows a start and end value to be specified; the WHILE/NEXT, which causes a block of code to be repeated while an expression is TRUE; and the UNTIL/NEXT, which repeats until an expression is FALSE.

Richard Lowe is manager of Technical Services at Software Techniques, Inc., an international consulting firm.


Connect with me

Unless otherwise noted, all photos and text is Copyright © Richard G Lowe, Jr.