![]()
It is basically a set of routines which provide Basic programming language with a
wider range of functions, thus enhancing its performance and transforming it into a
higher level language.
It is made of:
1. some routine libraries (.LIB and .QLB) suitable to any Basic 7.1 Microsoft program managing files, forms and reports. (From now on we will refer to Basic as QB, since routines used to be implemented in Quick Basic 4.5)
2. a package of utilities, running directly under DOS, to define files and
video forms, and other utilities, such as:
- re-compiling of exclusively those programs that have been modified after the latest
compilation;
- copying from Hard to Floppy Disk of only those programs that have been
modified after the latest comparison;
- searching for differences between two ASCII files (useful to compare two
different versions of the same program);
- inspecting all the programs of a directory searching for a single string
(useful e.g. to find all the programs using a given file);
- reading and updating data on files;
3. a RunTime option containing the most frequently used routines that can shorten your .EXE files by about 50-60%
![]()
These routines began to take shape in the early 80's.
At that time Basic, though in slightly different versions, was the only programming
language available on personal computers; file management was very limited.
In particular, indexes were not manageable.
Meanwhile, Mainframes were already supplied with large data-bases using indexed hierarchical and reticular structures, and the contrast between these powerful tools and the little offered by PCs was quite striking.
Therefore, after my first experiences on PCs, I decided I would find it very useful to have a few of the facilities already available on Mainframes added to them.
The opportunity arose when a friend of mine told me he needed an archive capable of including up to 8000 positions, each with its own access key made up of approximately 20 characters (from different fields) and a data range of about 10 characters. What was more, since hard disks were too expensive, he wanted his archive on a 280 Kbytes floppy disk!
Although the task was very hard, I set about it willingly, and in the end I built a
hierarchical archive made of 5 levels: to get to data level, at the bottom, you had to get
in from the top one and go through the other three, but access time was quite
acceptable.
On that first occasion, I made sure routines for archive management were kept quite
separate from the rest of the programs.
Later on, to face up to newer and newer needs, my routines widened more and more, to
handle reticular links and indexes, to keep programs apart from definition of data, to
manage video forms and reports. I even got to create a series of high level function.
I would like to list a few of the most important programs that in all these years have
helped me build my routines:
Naturally, it could be objected that many years have passed since the dawn of personal
computing and the situation is completely different, the market offers much more
powerful instruments than then, and that, therefore, the time has come to pension off
these routines.
However, the possibilities they offer, as well as the ease of the host language (for now,
Microsoft's Basic 7.1) have convinced me to stick with it and to believe that it could still
be useful to other people, too.
(See also
Enclosure 4 - Final considerations
which seems to me to back up this conviction).
![]()
Are above all:
- the use of the archives with operations, not foreseen by QB, that also
run reticular type structures and relational connections;
- the extreme simplicity of program maintenance, which depends, above
all, on the independence of programs from the layout of data in the archives;
- managing forms, with automatic association between on-screen data
and the archives;
- producing reports on-screen or for printing, put together with
macrofunctions;
- interference control if the environment is MultiUser.
Programs created by the user remain in the QB environment, and consist of
standard QB instructions mixed with MAIN-SYSTEM instructions. Remember that QB
does not require the word "CALL" when a routine is used, therefore the MAIN-SYSTEM
instructions are insertable within the normal QB instructions without
breaking the continuity.
Consequently, the only learning required of the programmer is limited to the few dozen
instructions provided by the MAIN-SYSTEM, which have mnemonic and intuitive
names; see
Enclosure 2 - MAIN-SYSTEM instruction set.
For example:
- to add a new record to a file, use the instruction "addrec";
- to rewrite in the file a record already read, use "modrec";
- to read the next record, or the previous one, of a chain in a reticular
environment (where "records" are called "members"), use "nextmm" or "priomm";
- to delete the current member of a chain, use "delmm";
- to read or write the
value of a field in a record, "getfld" or "putfld".
In cases where MAIN-SYSTEM is used with programs already written in standard QB,
the layout of the archives already in use can be maintained, at the same time gaining the
advantages of automatic management and data independence from the forms.
![]()
All those who operate on personal computers:
- analysts who need to plan complex archives on personal computers may find it
useful to define archive structures whose files accept the presence of indexes and both
relational and reticular connections with other files;
- programmers who can write programs in such an elastic way that any later
implementations, such as:
![]()
Using a MAIN-SYSTEM utility, for every packet of applications 99 different files can
be defined, each with a maximum of:
- 32767 records if it has an index;
- 10 millions records if it does not.
The limit of 99 files can be expanded, however, by defining many different groups,
each with a maximum 99 files, according to necessity.
The limit of 32767 records for every file with an index is not as restrictive as it appears;
in the paragraph on Reticular Archives a technique is described for drastically reducing
the number of records which require searching by index.
Anyway, such a limit depends on the management executed by variable integers; to
expand it, I may modify the routines with long integers.
Likewise, if the limit of 10 million (for files without indexes) appears too limiting,
since this depends on management by single precision floating, I may use long integers,
as above, to take the limit to beyond 2 billion records.
Every record can hold a maximum of 25 different fields; each field can carry "n"
elements of the same format, with "n" being a value between 1 and 32767: this
means that any field can be intended as an array of "n" elements.
The maximum number of fields which can theoretically be located in a record is therefore 25
x 32767, but the maximum length of the record, which is 32767 characters, must also
be taken into account.
The limit of 25 different fields can be overcome, where necessary, by a little stratagem:
just define two record types, each with 24 fields available and with one type exactly the
same length as the 24 fields of the other.
With each file you can:
- associate an index, to be managed automatically after having defined which fields
of the record are to compose it;
- connect other files in a reticular structure (see further on).
For every field you can:
- furnish a description;
- define it according to a certain typology, which includes all the formats of Basic
7.1 plus others;
- define the length;
- define the value "n" of the array above;
- define it as the key for another file (this enables in particular the routine forms
management to access the other file automatically and present a description of the
position identified).
Having defined the archive structure, with another utility you can print the files layout,
which for each file will show not only the list of fields, but also:
- which fields make up its key;
- which fields are the keys for other files;
- which reticular connections exist with other files.
![]()
On the files defined by the MAIN-SYSTEM the user program does not use words like OPEN, GET, PUT, CLOSE, FIELD/TYPE, CVx, LSET, MKx$. Any program may keep open contemporarily up to 14 files, with 25 different types of records. Instructions are available both for managing the records and for individual fields, depending on their format:
1. Records: there are 3 groups of instructions, related to:
1.1. "low
level": open file, close file, insert record, record variation, read record by number;
1.2. handling indexes: insert record, cancel record, read the key sequentially
backward or forward, random reading of keys of a certain value or of keys equal to or
higher than a given value, reading by minimum or maximum keys, key variation;
1.3. reticular environment: connection, disconnection, read forward/backward
along a chain, owner reading, cancellation.
2.Fields: initialisation of all the fields of a record, reading and writing of a
single field, or of the entire record seen as a single field.
(See Enclosure 2 - MAIN-SYSTEM instruction set
for details of instructions.)
The programs identify the fields inside the different record types by means of a
progressive number (starting from 1 for each record type), and, if necessary, an index
(to specify the array element); e.g, if the program needs to read the description of a
certain article, and supposing that description to be the second field of a record type 5,
you need an instruction like:
DesArt$ = GetFld$(5, 2)
so, if on this record, field 7 was an array with remainders at the end of each month,
to load the month "m" you would need to write:
Remain(m%) = VAL(GetFldj$(5, 7, m%))
![]()
Updating programs in this
environment is particularly simple notwithstanding variations in archives design.
Such variations require 3 interventions, on:
1. the archives design:
the intervention consists in using the MAIN SYSTEM utilities to redefine the archives.
2. the contents of the archives already loaded:
the archives already in existence must be converted to conform to the new design. This
is easily done by implementing the CNVARC program, as
necessary. This is an example of archive conversion connected to the MAIN-SYSTEM.
3. the programs already written: these interventions, which are normally the most
difficult, are kept to the minimum indispensible (see below) and sometimes
can even be completely omitted.
If the modified archive is referred to by a form, defined by the proper utility, and the
variation regards the design of the form, the same utility enables you to vary that
design.
In practice, once the programs have been written and the archives loaded, it may happen that on a certain record type:
1. NEW DATA is needed:
if it is logically and formally different from all the other data already present, it is
possible to insert it at the end of the record, otherwise if it is similar to a certain field, it
is possible to enlarge the array connected to that field, passing from "n" elements to
"n+1";
2. pre-existing data IS NO LONGER NEEDED:
it is possible to redefine it with zero length (it won't take up space in the archive) and
leave the field number free for further use;
3. pre-existing data CHANGES format:
you can change both the length and the physical structure; e.g. an integer (2 bytes)
which becomes a single precision floating (4 bytes).
The only variations necessary for the code of the programs concern (but not necessarily!) just the operations relative to fields inserted, cancelled or varied.
Viceversa, no intervention is ever necessary in programs which contain
only references to other fields in files which have been varied, even if
those variations meant physically moving those fields.
This is possible thanks to the method of determining data: in the previous example, for
the program which uses the MAIN-SYSTEM the remainder of month "m" is the field (5, 7,
m) and will remain such even if the description (field (5, 2)) should pass, for example,
from 20 to 30 characters.
Furthermore, if a program does not carry out particular elaborations on the varied
fields, but uses them only through routines of Forms
Management
and of Reports Generation, then it is probable that
only external interventions need to be carried out, by means of the appropriate utilities,
without modifying the program code.
In any case, those programs which have had no code variation have NO NEED
even to be recompiled.
For example: suppose a data-base has been constructed with numerous files,
which the user has already loaded with thousands of positions, in particular, suppose
that:
- the layout of the third file contains 15 fields;
- the fifth field is a description with 10 characters and appears only in a video
form;
- we need to lengthen that description to 20 characters.
The only interventions are:
- conversion of the existing file according to the new layout, modifying
CNVARC as necessary; (time necessary: a few minutes
to adapt CNVARC and a few minutes to execute it);
- variation of the record layout using the archives defining utility (less than 3
minutes);
- variation of the form that uses that field, employing the forms defining utility;
(time depends on the density of fields already present: if it is not necessary to redesign
the form, this means only defining the new length: 2 minutes).
With just these few interventions, probably in less than half an hour, the request has
been met without modifying or recompiling a single program in the packet.
![]()
Using the information contained in the general layout of the files, the system also
contains two alternative functions for managing the "FORMS" video and updating the
archives, including carrying out controls of the numbers or the dates where necessary.
(One of the 2 functions, the simplest, enables you to define a form by taking the
headings of the fields directly from the general layout of the archives.)
In a form it is possible to:
- use and update contemporarily fields belonging to different record types;
- define fields that have to be typed in every time, optional fields and fields which
never need typing because loaded automatically by the program or MAIN SYSTEM
routine;
- define fields which are keys of other record types: if the description
also appears in the
form, the MAIN SYSTEM routine verifies the existence of the key and loads the
description; e.g. if the code and description of an article appear in a form for loading
warehouse movements, when the terminal operator types a code, if it does not exist the
request is refused, otherwise the description appears;
- define fields that are not to be found in any record, not typed, but loaded by the
program.
The forms are defined outside the program, using a MAIN-SYSTEM utility.
The forms created ensure the complete freedom of the 4 directional arrows for moving
between fields, the Home key for positioning in the first field, the End key for cancellig
a field, the Ins and Del for inserting and cancelling characters in a field. In any case,
whichever key is touched the cursor remains within a typing-in field.
To use the forms, the user program needs only to follow the MAIN-SYSTEM routines.
E.g. there are two commands which:
- present a form on screen, filled with data already loaded by the
MAIN-SYSTEM;
- manage the updating of all data in a form: the MAIN-SYSTEM routine pilots a
cursor between one field and another, reads the data in records and rewrites them in the
records after a variation.
![]()
In the same way as forms are generated, another system function automatically creates reports, both on video and in print, by taking the data from every field of all the record types necessary.
![]()
These are archives consisting of records which are reciprocally connected by pointers
located on each record. They can form "chains" of homogeneous records (called
"members"), "hanging" from an "owner" record of a different type.
A simple example of a chain could be the management of a warehouse: there would be
two types of records, Articles and Movements, containing, for each article managed: 1
record on the Articles archive and n on the Movements archive relative to that article.
More precisely:
1. the Article record has an index which gives direct access to the Article wanted, and
contains connections to the chain of all the movements that regard it (in pratice, the
Article has pointers to the first and last Movement);
2. the Movement record has no index, but is identified simply by a progressive
number, and forms part of a chain hanging from an Article, that is, it contains pointers
to the next Movement, the previous one and to the Article itself.
Therefore, when looking for all the movements of a certain Article, first locate the
Article record and then run down the chain of that Article, which contains all the
movements wanted, and only those.
This means that if, for example, there are 100,000 Movements, but those relevant to a
certain Article are only 3 (e.g. numbers 151, 38498 and 95004), starting from the A
you quickly find M 151, which is directly connected to M 38498, which leads directly
to M 95004, ignoring all the other Movements.
It is also possible, starting from A, to find the last M directly (this normally being the
most recent) without running down the chain; this possibility is particularly useful when
the chain is very long.
With this type of structure you can avoid managing large archives with
indexes: in the example seen, there could be a few thousand Articles (with index)
and many thousands of Movements (without index, but still connected to the Articles).
Furthermore, you can also manage over-lapping chains, in the sense that one record
may be not only an owner of many chains, but also a member of many
chains, and you can "navigate" in the data-base as necessary.
In general, in a structure with numerous record types it is possible to create a
connections reticule, enabling you to start from a record and move in all directions and
towards other record types, simply following the chains; these reading operations
are always very fast, because the pointers give the exact locations of the records
to read.
Now that the general concept of reticular archives has been explained, it should be
noted that for every record type defined in the structure, the MAIN-SYSTEM allows
you to:
- hang up to 5 different chains on it;
- hang it on 5 different chains.
This means that every one of the 99 possible record types can be connected by chains to
another 10 record types, too.
(It should also be remembered that every record type can be connected logically to other record types, if some of its fields have been defined as "keys" of those types).
![]()
11. Example of a Reticular Archive
Let's look at a simple case of interconnecting chains.
A company stocks the same Articles provided by different Suppliers; each S
sells various A., each of which at a certain price.
We need an archive that can quickly respond to questions of the type:
- given an A., who are all the S that offer it, and what are their prices;
- given a S, what are the A offered, and at what price.
The reticular solution requires 3 files:
- an anagraphic file of the A., containing the description and any code of the A.,
which are used as an access key if the A file has an index; if it does not, each A would
be identified simply by its progressive number in the A file.;
- an anagraphic file of the S, containing a description and any code of the S, which
are used as an access code if the F file has an index;
- a "connector" file, containing the prices; each record is connected to both an A
and a S..
Intuitivly, you can imagine a structure where from every A and every S there hangs a
chain of "n" connector records; each connector belongs to a chain hanging from an A
and to a chain hanging from a S, therefore establishing a connection between an A and
a S; the following diagram illustrates the simplest case, in which there is only one A
and one S.:
Article Supplier
o o
o o
o o
Prices
Now, let's consider an alternative hypothesis, not involving the MAIN-SYSTEM, but
using other languages, which use files indexed
with alternative keys, or D.B. relational.
In this case, too, it would be useful to define 3 files, the first two for the A and the S,
the third would have to contain in each record not only the price, but also the codes for
the A and S, and be equipped with two access keys:
- one containing, in order, the A and then the S;
- the other with the S and then the A.
To answer, for example, the question "who are all the S of a certain A code", you need
to use, in order:
- the first archive, just to get the description of the A;
- then the third archive,
to find all the lines relative to the A requested;
- for every line found in the third archive, in order to read the name of the S you
need to access the second archive with the S code key
Naturally, a high level language, operating in a relational environment, would not
expect the programmer to execute all these elaborations, but, ultimately, these
operations must be carried out by the language.
The answer to the same question, using the reticular solution proposed by the
MAIN-SYSTEM seems less difficult for the elaborator:
- use the A code to
access the first archive, to get the description of the A;
- from here, run down the chain of Prices hanging from the A.;
- from every
Prices record present in the chain, you can access directly (without key) the S, who
supports it on the other chain, and read off the name.
Some final remarks:
- every connector record is directly "pointed to" by the A, or by the previous
connector;
- the S is directly "pointed to" by the connector.
However, THE ONLY ACCESS WITH KEY is that executed on the A at the
start of the work.
Considering that, of access by key and access by pointer the first type is certainly more
time-consuming to carry out, in cases like that described the reticular system is more
efficient in achieving the desired result.
![]()
Please, send your impressions or suggestions to:
[email protected]
![]()