GFK 0256 D
GFK 0256 D
GFK 0256 D
1-800-360-6802
sales@pdfsupply.com
GE Fanuc Automation
MegaBasic Language
Reference and
Programmer’s Guide
Reference Manual
GFK-0256D September 1994
GFL–002
Warnings, Cautions, and Notes
as Used in this Publication
Warning
Warning notices are used in this publication to emphasize that hazardous voltages,
currents, temperatures, or other conditions that could cause personal injury exist in this
equipment or may be associated with its use.
In situations where inattention could cause either personal injury or damage to
equipment, a Warning notice is used.
Caution
Caution notices are used where equipment might be damaged if care is not taken.
Note
Notes merely call attention to information that is especially significant to understanding
and operating the equipment.
This document is based on information available at the time of its publication. While
efforts have been made to be accurate, the information contained herein does not
purport to cover all details or variations in hardware or software, nor to provide for
every possible contingency in connection with installation, operation, or maintenance.
Features may be described herein which are not present in all hardware and software
systems. GE Fanuc Automation assumes no obligation of notice to holders of this
document with respect to changes subsequently made.
GE Fanuc Automation makes no representation or warranty, expressed, implied, or
statutory with respect to, and assumes no responsibility for the accuracy, completeness,
sufficiency, or usefulness of the information contained herein. No warranties of
merchantability or fitness for purpose shall apply.
E Copyright 1992 by Christopher Cochran.
All rights reserved. No part of this manul nor the software it covers may be reproduced
or copied in any form or by any means – graphic, electronic, magnetic, or mechanical,
including photocopying, recording, taping, or information retrieval systems – without
written permission from the author.
GFK-0256 iii
Preface
iv MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
Preface
GFK-0256 Preface v
Preface
vi MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
Contents
GFK–0256D MegaBasic Language Reference and Programmer’s Guide Reference Manual – vii
September 1994
Contents
GFK–0256D MegaBasic Language Reference and Programmer’s Guide Reference Manual – viii
September 1994
Contents
GFK-0256 1-1
1
automatic selection of software vs. 80x87 math coprocessor in IEEE versions (Chapter 3,
Section 8). Multi-dimensional arrays of integers and real numbers have no set limit
on how much memory they can use (Chapter 3, Section 1).
h Extended numeric assignment statements letting you assign values to variables
within numeric expressions and to perform increment, decrement or other
arithmetic operation on variables (e.g., X+=Z,Q/=D,Y*=M, etc.).
h A complete family of integrated arithmetic and mathematical vector operations for
dramatic reduction in both execution time and notational complexity for matrix
processing and other general sequential processing of integer and real numbers
(Chapter 3, Section 7).
h Field structures let you assign names and data types to specific regions within string
variables or other fields so you can later refer to these fields with pathnames and
access them as variables for any purpose (Chapter 5, Section 3).
h Supports pointer extraction and resolution on variables, arrays, strings, fields,
procedures and functions, similar to C or PASCAL pointer capabilities but with better
dynamic support (Chapter 5, Section 4).
h Extended, integrated library of character string and bit-string operations, including
pattern matching and search, re-ordering and rotation, format conversion, character
translation, set searching, enumeration, union, intersection and exclusion. Large
strings and string arrays supported and no garbage collection penalties (Chapter 4,
Section 1).
h True multi-level error trapping that lets you trap errors at any level or pass errors on
to higher level as needed (Chapter 6, Section 4).
h Supports shared/exclusive open files and file region locking in network and multi-user
environments (Chapter 7, Section 2)
If you are reading this section for instructions on how to RUN a MegaBasic program and
have no interest in the details of actual programming, skip this section and move on to
Section 2 in this chapter.
1-2 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
1
The MegaBasic software system comes with one user’s manual and a diskette containing
all the software components. Some of the more important files are described below:
There may be some slight variation in the precise disk contents and spellings of the file
names; the above list is intended to be a rough guide rather than an exact table of
contents. This is because MegaBasic is supported on a wide variety of machines and
operating systems and the disk contents are much more likely to change over short
periods of time, as compared with the printed documentation.
INSTALL
After INSTALL completes, remove the diskette(s) and re-boot your computer, which
applies any new configuration to the running system. From then on, you can load
MegaBasic and access program files from the PGM directory without having to specify
any directory path names (regardless of the current default directory).
1-4 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
1
MegaBasic is an executable file which you RUN by typing its file name from the console as a
direct command to the operating system. It must reside on one of the system disk drives
installed on your computer system in order to be executed. The specific command to
invoke MegaBasic merely consists of the file name containing MegaBasic followed by the
name of the file containing the BASIC program you wish to execute. File names are not
fixed entities and particular applications may have file names assigned which differ from
those stated in this manual. Assuming MegaBasic is contained in a file named BASIC and
your program is named MYPROG, the command to execute your program from the
operating system is as follows:
BASIC MYPROG
This command causes the operating system to load MegaBasic which in turn loads your
program file and then begins its execution. At that point your program takes over the
computer and proceeds with whatever it is programmed to do. The MYPROG program file
contents must have previously been created by MegaBasic.
1-6 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
1
To use the MegaBasic program development environment, type the BASIC command
described earlier, but omit the program file name. Without a program name, you
immediately ENTER into the command mode of MegaBasic which under your direction
provides facilities to create and test programs. Only BASIC (the development version)
provides this command mode, while RUN (the runtime production version) does not.
The command mode provides a selection of over 20 commands, which you choose and
ENTER from the keyboard. Each command specifies a single task which MegaBasic
carries out immediately after accepting the command. The command set can be divided
into four logical groups:
h Program Entry & Retrieval
entering programs from the keyboard or from files, listing your programs on the
console or other devices, saving your programs to files.
h Editing & Alteration
Sequential line editing, global search and replace, identifier renaming, line
renumbering, line range deletion, rearranging program sections, merging program
modules from files into your current program.
h Execution Control & Debugging
Running and testing, debugging by breakpoint and single-step debugging,
interrupting and continuing execution, interactive examining and setting of program
data structures.
h Information and Control
Displaying program statistics, listing file directories, exiting back to the operating
system command level, switching between multiple programs in memory,
displaying execution state.
After entering the MegaBasic command mode, the first thing you do is either key in a
program from the console or load an existing program from a file. To type new program
lines from the console, enter a line number (an integer from 0 to 65535), followed by a
sequence of program statements separated by semi-colons and terminated with a
carriage return. Lines may be up to 255 characters long. The line number tells
MegaBasic where to insert the new line into the current program. Therefore new lines
may be entered in any order, providing a simple way to insert changes at a later time.
See Chapter 1, Section 4 for further details on MegaBasic program format.
Any line typed with a valid line number is always inserted into the current program; if
there is no current program the line becomes the first program line. If the line number
duplicates a previously existing line number, that line is replaced with the new line. All
lines entered without line numbers are assumed to be commands or direct statements that
MegaBasic attempts to execute immediately regardless of their actual contents.
MegaBasic will inform you of lines which contain improper statements or commands
when they are typed for immediate action.
After entering or loading a program and making any desired changes, you can then run the
resulting program under interactive control of execution to check its correctness. If errors
1-8 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
1
are found, you can alter the program to correct the errors, and then repeat the process until
you are satisfied with program operation. At any stage of the development phase, the cur-
rent program may be saved on a disk file to safeguard your work from system failures or
your own blunders (e.g., power failures, mistaken revisions), or so that you may continue
work at a later time. On completion of your working program, save the final version on a
file to be executed as described in Section 2 of this chapter.
MegaBasic always maintains a file name in connection with your program. This file
name is the one used to load the program from the disk or the one used to save the
current program onto the disk. A program entered from scratch at the keyboard is
assigned the name unnamed.pgm.
MegaBasic keeps track of this file name for two reasons. First, you can save your
development work out to the file without having to remember its name yourself or to
type it correctly each time, which saves time and eliminates potentially destructive
mistakes. Secondly, MegaBasic lets you have as many as 64 programs in memory
simultaneously and the file name associated with each provides a name through which
they may be accessed at random. Each program source has its own workspace (in
memory) in which development activities may take place. This capability is extremely
powerful for large scale program development and execution purposes, but its detailed
description is beyond the scope of this section and will be covered later on in Chapter 10.
MegaBasic programs consist of a series of typed lines beginning with a line number and
ending with a carriage return. Line numbers must be in the range 0 to 65535 and serve a
dual purpose. First, since MegaBasic continually keeps the program lines arranged in
ascending order, you can easily insert additional lines by typing them with appropriate
line numbers. Secondly, some MegaBasic statements refer to program steps by line
number, perhaps to repeatedly execute some group of statements or skip over undesired
statements. The simple example program below illustrates some of the building blocks
used to form programs:
100 REM *** This is a sample Program ***
110 INPUT “ENTER a number -- ”,N; If N<=0 then Stop
120 Print N, N*N, Log(N), Tan(N), Sqrt(N), Atn(N)
130 Goto 110
Line 100 contains a remark which describes the program to a human reader and is
ignored by MegaBasic when executed. Such remarks may appear anywhere in a
program to document program operation. Line 110 contains two statements, separated
from each other with a semicolon (;). The first statement causes the computer to display
the request ENTER a number—and accept a number from the user when he/she is ready to
type it in. The second statement on line 110 stops the program if the number entered is
less-than-or-equal-to zero. Line 120 goes on to display various computations on the
value entered, but only if the value is greater than zero. Line 130 causes the computer to
go back to line 110 and ask for another number, which repeats the whole process until
the number entered is not greater than zero.
Besides being numbered, the lines themselves may be up to 255 characters long and
consist of one or more statements (i.e.. you cannot have a line with no statements on it).
Statements are separated from one another in the line with semi-colons (;) and represent
the fundamental building blocks of MegaBasic programs. Statements in general begin
with a specific keyword followed by additional data parameters separated from one
another with commas (,). For example the PRINT statement above begins with the
keyword PRINT and it is followed by a list of things to be printed.
By themselves, statements perform simple and easily understood operations, but in
combination they can express procedures of unlimited complexity. MegaBasic
statements are grouped into six Chapters (Chapters 5 through 10), each beginning with a
summary of the statements they contain, followed by detailed descriptions of each
MegaBasic statement.
1-10 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
1
carriage return) to insert empty blank lines for visually separating successive sections of
your program. A line-feed may be typed anywhere a space is permitted. No line may
be longer than 255 characters, regardless of line-feeds.
A key feature in MegaBasic is the way it lets you to assign meaningful names to any
program line, variable, function or subroutine. For example, the name CUBE_ROOT is
certainly more descriptive than FNR3 for a user-defined function that computes cube
roots. Names must conform to certain rules in order to be properly recognized. The
syntax of user-assigned names in MegaBasic is simple, reasonable and easy to
remember:
h Names must begin with a letter (A-Z).
h Characters after the first must be letters (A-Z), digits (0-9), or underscores (_).
h The last character of a name may be a dollar sign ($), a percent sign (%) or an
exclamation mark (!) to force the data type of the name to string, integer or floating
point, respectively. Other methods exist to declare the data type of a name without
such characters.
h Names may be from 1 to 250 characters in length and all characters participate in the
spelling and must be present in all references.
h Upper and lower case letters in names are treated identically.
h MegaBasic reserved words (e.g., FOR, NEXT, READ, etc.) cannot be used for
user-assigned names. See Appendix D-2 for a complete list.
Examples of valid names are TOTAL!, X3, THIS_IS_A_NAME, and STRING$. Examples
of illegal names are 3X LABEL#, $VAR, X$STR, and THIS&THAT. Underscores are
useful for breaking up longer names since spaces are not permitted. All characters in a
name are significant in recognizing the name, i.e., two names are different unless they
match exactly. Upper and lower case letters are treated identically so that you can type
names with or without the SHIFT key.
Line-labels are names which may optionally be typed at the beginning of any program
line (after the line number). Such lines may be referred to either by line number or by
name. For example, the following one line program prints all the integers from zero to
one hundred:
10 AGAIN: Print C; C = C+1; If C<101 Then AGAIN
Notice the colon (:) after the AGAIN line-label. A colon must always follow each
line-label definition immediately without intervening spaces. Line label references are
never followed with a colon. The colon is required to clearly distinguish line-labels from
other named objects used in the program.
This example uses a variable named C which is displayed and incremented by the
program. Regardless of how you type in a program, when it is LISTed user-assigned
names always appear capitalized and MegaBasic reserved words appear in lower-case so
that you can see which are which. This is important because reserved words cannot be
employed as user-assigned names. Hence when you see one of your assigned names
spelled with any lower case letters, you will know that it is a reserved word, an error
that must be rectified by editing the program. This kind of editing is best performed
using the CHANGE command (Chapter 2, Section 3).
Variables and functions with names ending in a dollar sign ($) are automatically string
variables and string functions. A percent sign (%) ending names of variables and
1-12 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
1
functions gives them an integer data type and an exclamation mark (!) forces a real
floating point type. You can assign data types to various letters so that variables and
functions with names beginning with those letters will automatically be defined with the
data type specified. This subject is covered further in Chapter 3, Section 1.
The NAMES command (Chapter 2, Section 3) displays the user-assigned names in your
program. It is sometimes useful for finding occurrences of names which have been
misspelled or mistyped during the course of editing your program. Since the NAMES
display is alphabetically ordered, names which are similar tend to be together in the list
and it is generally a simple matter to visually scan the list to find similar but different
spellings.
If you do not correct such misspellings, each different spelling will refer to a different
program variable, function or procedure, and your program will not operate correctly.
Another way to detect such errors is by displaying a cross-reference listing of your
program, using the XREF command (Chapter 2, Section 5). This command finds all
references to each user-assigned name throughout your program. Since virtually all
names will be used in more than one place, any names that are only referred to once are
likely misspellings of other names. XREF should be used for this purpose after you make
any major additions or alterations to your program, so that you can correct any
misspellings before you even begin testing your program again.
Whenever you ENTER data or program lines from the keyboard you are actually using
the MegaBasic line editor. This line editor lets you ENTER lines of text, and provides
editing services ranging from simple typing corrections to text insertion, searching, block
deletion and rearrangement. It provides a visually complete presentation of the line you
are modifying at every key stroke, while supporting virtually all video screens (i.e.
IBM-PC screens and generic terminals) without any configuration. This makes it suitable
for use over modem communication lines and a wide variety of hardware
configurations.
All editing functions are invoked by typing special control or function keys. Not all keys
perform editing functions and if accidentally struck will be rejected by the computer
with a warning beep. For the purpose of notation Ctrl- ? will denote a control character
where ? is some key.
If you don’t make any mistakes while typing an input entry, then all you have to do
when your input line is finished, is type the ENTER (or RETURN) key. You can easily
correct simple typing errors by backing up over the error with the BACKSP key, type the
correct characters, then continue the input entry. In the pages that follow, we will
explain how to use other line editor control keys to insert text, delete and rearrange text
blocks, move the cursor, and search for characters.
Inserting Text
Some editors provide two different ways to input characters: insert mode and
replacement (or overwrite) mode. This forces you to remember at all times what mode
you are in. To make things easier, the MegaBasic editor is always in insert mode. This
means that whenever you type characters while inputting or editing a line, the
characters you type are always inserted into the line at the cursor location. To replace
characters in your line with a new sequence of characters, you have to delete the old
sequence then type the new sequence.
The cursor is the special screen symbol that indicates the location where the next
character will appear. Normally, this will be at the end of the line you are typing.
However, you can move the cursor to any point within the line you are editing, so that
subsequent characters you type will be inserted into the line instead of appended to the
end of it. Cursor repositioning is summarized on the next page.
When the cursor reaches the right margin of the screen and you continue to type more
characters, the cursor will wrap around to the next screen line below it and continue on.
This will generally break up your input entry in an arbitrary place. You can insert you
own line break anywhere in the line by typing a line-feed (down arrow or Ctrl-J). This
breaks the line, moving all text past the cursor down one line, and positions the cursor at
the beginning of the next screen row and enters a line-feed code (an ASCII 10) into the
input line.
Most input entries will be less than 80 characters and will generally fit completely on one
screen line. However, MegaBasic lets you type a line of up to 255 characters. Once this
limit has been reached, MegaBasic prevents you from entering any more characters and
beeps at you each time you try to insert a character. At that point you either have to
delete characters from the line to make room for more input, or enter the line the way it
is.
1-14 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
1
Cursor Positioning
MegaBasic provides a variety of ways to move the cursor to a different location within
the current input line. Changing the cursor position does not alter the line in any way,
nor does the position affect the input entry when you type the ENTER (or RETURN) key to
terminate it. The only reason to move the cursor is so that a subsequent insertion or
deletion can take occur at the right place.
Two controls let you move the cursor left and right by one character (the left and right
arrow keys or Ctrl-L and Ctrl-A). Two other controls let you move the cursor left and
right by one word (Ctrl-left and Ctrl-right arrow keys or Ctrl-W and Ctrl-Q). A word in
this context is any sequence of letters and digits containing no other characters. Moving
left or right a word always leaves the cursor on the first character of the word. By typing
these keys repeatedly you can walk through the line to quickly locate the position where
you want to make a change. Two other controls let you move to the beginning of the
line (Home or Ctrl-F) or to the end of the line (End or Ctrl-G).
Another control, F2 (or Ctrl-S), lets you advance the cursor to the next occurrence of any
single character. After you type it, you must then type the character you wish to find. If
it exists, the cursor moves to that character in the line, if it does not exist, the cursor does
not move and a warning beep sounds. If you type this control twice, it will search for the
same character that it searched for the last time. When you search for a letter, you can
type it in upper or lower case regardless of the case of the letter sought.
An important aspect of entering and editing program lines is making sure that all your
parentheses and brackets are properly balanced. In complicated lines containing many
levels of parentheses, it can be difficult to see where each parenthetical sequence begins
and ends. Therefore, MegaBasic provides two keys to move the cursor between opening
and closing parentheses. F9 (or Ctrl-O) backs up the cursor to the preceding
parenthesis, bracket or brace. If the cursor is already on a closing parenthesis, bracket or
brace, it backs up to the opening parenthesis that matches it. F10 (or Ctrl-P) is the
reverse of F9, advancing the cursor to the next parenthesis, bracket or brace in the line.
If the cursor is already on an opening parenthesis, bracket or brace, it advances to the
closing parenthesis matching it. If no matching parenthesis exists in the line, the cursor
does not move and a warning beep sounds.
In order to promote the widest possible console compatibility, MegaBasic relies on only
the minimum possible set of console controls to position the cursor. Only one operation
requires any configuration: backing up the cursor to the previous line. This is controlled
by the Console Mode byte, which you can configure using the CONFIG utility program,
described in Appendix C, Section 3. If you have trouble with the line editor maintaining
the proper cursor position or observe any erratic behavior, consider trying a different
configuration.
Deleting Text
Deletion is always relative to the cursor position. BACKSP deletes the character to the
left of the cursor; DEL deletes the character at the cursor location. F6 (or Ctrl-V) deletes
all the characters from the cursor to the next word. F4 (or Ctrl-X) followed by a character
deletes from the cursor up to that character, or beeps if the character is not found in the
line. Typing this F4 twice deletes up to the next occurrence of the previous search
character. Ctrl-HOME deletes all characters to the left of the cursor; Ctrl-END deletes all
characters from the cursor position to the end of the line.
1-16 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
1
whenever you are entering a command or entering keyboard input. This is particularly
useful when you find yourself entering several different complicated commands or
inputs repeatedly, since you can avoid having to retype them each subsequent time.
MegaBasic only remembers one instance of each line entered and keeps them in a
most-recently-used order for convenient access. Lines that differ only in upper/lower
case and number of spaces are treated as the same line and only the most recent
rendition is remembered. Null lines (i.e., those without any characters) are never
retained.
You access previously entered lines by typing one of several control keys at any time
while you are entering a text line into MegaBasic (or into a MegaBasic program). PgDn
and PgUp keys move forward and backward through the line list; F5 returns to the
original line and Ctrl-D deletes the current line from the list. Once a line is accessed, you
can immediately begin editing it without any further keystrokes. At any time you can
discard your current line and start over on a different line by simply accessing another
line and continuing.
When accessing previous entries with PgUp and PgDn keys, the characters to the left of
the cursor are used as a matching criteria, selecting only the entries that begin with those
same characters. As each line is accessed, the cursor is left in the same position so that
you can step through different lines beginning with that sequence. A warning beep
indicates no entry begins with such a sequence. If the cursor is at the front of the line
(i.e., no characters to match), PgUp and PgDn keys step through every line.
The number of lines retained depends on how many lines fit into the previous line
buffer. This buffer defaults to 512 bytes, but you can change its size to any value from 0
to 4096 bytes by setting PARAM(24) to the desired size at any time Setting the buffer size
to zero disables the previous line list capability altogether (except for the standard old line
buffer). Setting PARAM(24) always clears the buffer of all lines, except for the most
recently entered line. Defining a larger or smaller buffer size causes the total available
memory space to decrease or increase accordingly.
If there is not enough room in the previous line list buffer for the next line being added
to it, MegaBasic makes room for it by deleting the oldest lines in the buffer until sufficient
room becomes available. If the line length exceeds the entire buffer capacity, the line will
not be added to the list. Therefore to use this capability effectively, your buffer size (as
defined by PARAM(24) should be at least as large as the longest line you will ever want to
retain.
When you are modifying your program under the EDIT or ENTER command modes, the
entered source lines can quickly fill up the previous line buffer and displace some or all
of the prior command lines that you have typed. Therefore MegaBasic only remembers
the single most recent program source line that is entered while in these modes. If you
want to be able to access other such lines in later editing or input, you can always force
the current line into the buffer by typing Ctrl-B just before typing RETURN to enter the
line.
The EDIT$ function always returns the most recent line so far entered. Setting EDIT$
(e.g., EDIT$ = string), adds a new most-recent line to the line list. Setting EDIT$ several
times in succession adds several lines to the list, which can be useful for pre-loading the
buffer in preparation for a subsequent input entry.
The preceding discussion provides a complete explanation of the MegaBasic line editor,
it capabilities and the editing process in general. The table below summarizes all of the
editing control keys provided by MegaBasic.
For convenience, alternate keys are provided for most editing operations. In particular,
the editing and cursor controls provided by the IBM-PC and PC BASIC are represented
along with a generic control-character set that will work with any console terminal.
Control characters are typed by pressing a specific character while holding down the key
labelled CTRL on the left of the keyboard (the SHIFT key may be up or down). The
IBM-PC set consists of function keys F1 through F10, the HOME, END, TAB keys and the
cursor direction arrows (denoted Left, Right, Up and Down). These keys are supported
for the editing functions below only for IBM-PC compatible keyboards. Other keyboards
may appear to have these keys but the actual codes they generate may not be the same.
If the indicated action for a editing key cannot be completed by MegaBasic for any
reason, a warning beep is sounded.
The controls described below are line-oriented and their actions are confined solely to
the current line being input or edited. When you are editing program source code, each
line you are editing is under control of this line editor as a stand-alone line. There are
currently no controls that provide a full-screen editing facility within MegaBasic (e.g.,
you cannot move the cursor freely between separate program lines). A line may be
broken up into more than one screen line with line-feeds or by entering characters past
the end of the screen to cause a wrap-around to the next screen row. Although such a line
appears to be multiple lines, you should treat it as the single line that it is.
Character Operation
Right Moves the cursor one column to the right or to the next line if a line-feed is encoun-
F1 tered. This does not modify the current line. A warning beep will sound if you are
Ctrl-A at the end of the line when you type this control.
Left Backs up the cursor one column to the left. This can be repeated to backspace
Ctrl-L all the way back to the beginning of the line. It also backs up through line-feeds
embedded in the line.
Backsp Deletes the character to the left of the cursor. All remaining characters in the line
Ctrl-H that follow are shifted left one column to close the gap. Line-feeds and TABS can
Rubout . be deleted just like any other character
Del Deletes the character from the line at the cursor position and shifts all characters
Ctrl-Z that follow it over one column to close the gap. The cursor does not move. Line-
feeds and TABS can be deleted just like any other character.
Word Operations
Ctrl-Right Advances the cursor forward to the beginning of the next word in the current line,
F8 where word is defined as any contiguous sequence of letters and/or digits. This key is
Ctrl-Q useful for quickly skip ping through the line to some point of interest.
Ctrl-E
Ctrl-Left Backs up in the line to the beginning of the previous word, where a word is defined
F7 as a contiguous sequence of letters and/or dig its. If the cursor is in the middle of
Ctrl-W a word, it backs up to the be ginning of that word.
F6 Deletes all characters up to, but not including, the first character of the next word,
Ctrl-V where word is defined as any contiguous sequence of letters and/or digits. The text
to the right of the deletion moves over to the left to close the gap.
1-18 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
1
Searching Operations
Advances the cursor up to the character that you type immediate ly after this key.
F2 Upper and lower case letters are equivalent when searching. If the specified
Ctrl-S character is not in the remainder of the line a warning beep is sounded and the
cursor does not move. This is a two stroke sequence and typing F2 twice will
repeat the previous F2 search sequence.
Deletes all characters from the cursor position up to, but not in cluding, a specified
F4 character. Like F2 above, F4 is a two-stroke sequence and typing F4 twice will
Ctrl-X repeat the previous F4 deletion sequence.
Backs up the cursor to the preceding parenthesis, bracket or brace. If the cursor is
F9 already on a closing parenthesis, bracket or brace, it backs up to the opening
Ctrl-O parenthesis that matches it.
Advances the cursor to the next parenthesis, bracket or brace in the line. If the
F10 cursor is already on an opening parenthesis, bracket or brace, it advances to the
Ctrl-P closing parenthesis matching it.
Line Operations
End Advances the cursor to the end of the current line. Further input after this control
F3 will append to the end of the line.
Ctrl-G
Home Repositions the cursor to the beginning of the line, regardless of its current location.
Ctrl-F
Ctrl-End Deletes all characters from the cursor position all the way to the end of the line.
Ctrl-N
Ctrl-Home Deletes all characters to the left of the cursor all the way back to the beginning of the
line.
Edit Control
ENTER Terminates the edit, moves the cursor to the end of the input line, adds the line to
RETURN the previous line list and returns the entire line to process requesting the input.
Erases the line from the screen, abandons the line edit and terminates whatever
Ctrl-C process is currently underway. This key does nothing during program execution if
Esc Ctrl-C is disabled.
When you are in the MegaBasic program EDIT mode, Ctrl-K will abandon the cur-
Up rent line you are editing and begin editing the line that immediately precedes it in
Ctrl-K the program. When you are in the ENTER mode (automatic line numbers), Ctrl-K
will abandon the current line being entered and go back to the previous line and let
you edit it.
This is an undelete key. It inserts the last contiguous sequence of deleted characters
Ctrl-U back into the line at the cursor position. It is useful recover deleted characters or to
move or copy char acter sequences from one place to another, even between separate
entries.
Line Formatting
Down Forces a line break during an input entry without terminating it. In MegaBasic, an
Linefeed edited input entry can be up to 255 characters long. Therefore this key lets you
Ctrl-J break long input entry into several physical lines by entering line-feeds into the
input line.
Advances the cursor and any text that follows it over to the next column position
divisible by 8 (i.e. 8,16, 24,...). The key enters a single character into the input string
TAB (an ASCII 9 code), rather than a series of spaces. Tabs are permitted in program
Ctrl-l lines anywhere that spaces are permitted or as separators between numeric inputs.
They are useful in program lines for indentation and other significant white space
without eating up the line capacity (255 characters maximum) the way spaces do.
1-20 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
Chapter 2 MegaBasic Commands
2
Chapter 2 gives information about the MegaBasic commands in general and the ideas
common to several or all of them. This includes such topics as the multiple workspace
environment, the notational conventions used to describe MegaBasic statement and
command syntax, device numbers, search strings, etc.
GFK-0256 2-1
2
2-2 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
2
The command mode provides a selection of over two dozen commands, which perform
such things as loading and saving program files, modifying programs, displaying
information about the program state, running programs, etc. Each command specifies a
single task which MegaBasic performs after you type in the command. You can perform
any complex task, such as developing and debugging a MegaBasic program, by typing
individual commands, one by one, until there is nothing left to do.
Before describing the various MegaBasic commands, we will first explain the concepts
involved in forming commands and how to use them within the MegaBasic workspace
environment. Some of the things discussed in this introduction include specifying
program line ranges, output device channels, string search patterns, understanding
command and statement syntax notation, and program file names.
Encloses an item that is optional. For example [# ] means that you may type an
optional Ib-sign (#) in that part of the statement or command, and [<sfring expres-
sion>] means you type an optional string expression. The [...] brackets may
[...] contain several items, in which case you either type all of them or omit them all.
You will also encounter bracketed items inside of outer brackets to indicate
optional items within larger items which are themselves optional.
Encloses a list of items from which you choose one item. For example {STOP END
{...} ON} means you type one of the words STOP, END or ON. The {...} braces may
include <...> items as well.
All letters, digits and punctuation are otherwise typed exactly as they appear in the
command description. The actual bracket characters themselves (i.e., < >, { } and [ ])
are not typed into a command or statement, as they are shown simply to help describe
their syntax. However, there are a few places where brackets [ ] are specifically used in
2-4 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
2
statements (e.g., IF and STRUCT statements, vectors, etc.), but each case is specifically
documented to avoid confusion. When any of the special brackets (and any descriptions
they contain) are used to delineate syntax, they are shown in italics, otherwise they are
shown in normal or boldface to indicate literal usage. The following examples should
clarify how to type specific commands from their syntactic descriptions:
EDIT
EDIT [<starting line> [,<string>]] EDIT 1000
EDIT 150, find this
ENTER
ENTER [<starting line.> [,<step>]] ENTER 100
ENTER 300, 20
CLEAR
CLEAR [DATA]
CLEAR DATA
sequence number). A dollar sign ($) refers to the last line of the program. A dot (.) refers
to the most recent line displayed by MegaBasic or edited. You can specify a line using any
of these three forms in line ranges or in any command where MegaBasic expects to see a
line number.
In executable program statements that refer to lines (e.g., GOTO statements), you may
optionally refer to the intended line by line-label, if that line contains a line-label. You
cannot specify line-labels in MegaBasic commands for any purpose. Line-labels make
the program much more readable and easier to develop and maintain. Chapter 1,
Section 5 shows how to define and use line-labels and other named program entities.
2-6 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
2
The REN, MOVE, COPY and DUPL commands set the .. range to the range of program
lines that they affected. You can specify the .. notation in all commands that act on line
ranges. By experimenting with the .. notation you will find ways of using it to
streamline the process you go through to develop and maintain your program source.
You can append an additional parameter string to an EDIT, LIST or CHANGE command
to enable or disable additional search capabilities. This optional argument consists of a
comma followed by one or more single-character option switches. Each option switch
character turns on or off a different feature. If you never specify an option string
argument, all options remain off (i.e., disabled). Once you switch an option on, it stays
on for all three commands until you explicitly turn it off in a subsequent option string
(using the minus (–) option described below). All available option switch characters are
individually described below:
2-8 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
2
Option strings are never quoted when they are actually typed. The example above
shows them quoted only for descriptive clarification. MegaBasic reports an Argument
Error if you type any other characters in an option string, except spaces. To further
enhance your understanding of the any-string character in search and replacement
string, a number of example CHANGE commands (abbreviated CH) follow below. Each
example assumes that some previous command has turned on the ( * # options.
Example Change
Command Result Accomplished
CH rem*, rem Deletes the text of all program remarks.
Inserts an additional subscript between the
CH “X(*,*)”, “X(*,S,*)”
first and second existing subscripts of all ref-
erences to array X( ). Notice that quotes are
needed to allow commas to be part of the
string.
CH “X(*,*,*)”,“X(*1,*3)”
Deletes the second subscript expression from
all references to array X( ).
CH “fn(*,*)”,“fn(*2,*1)”
Moves the leading parameter of function
fn( ) to the end of the list in all references.
CH“*;*;*;”,“*3;*2*1;”
Swap the first statement with the 3rd state-
ment on every line.
If the file is not on the default drive, you must include the appropriate drive letter in the
file name. You specify the drive letter in front of the file name, separated by a colon (:).
File names and drive letters can be in upper or lower case with the same effect. For
example the following file names all refer to the same program file on drive B:
TAB
Displays the next 10 output lines of the
current display on each keystroke. Effec-
tive only during a pause.
Ctrl-C or ESC Immediately terminates the listing.
You should, in particular, be prepared to press the space-bar immediately after giving the
LIST command if you are using a fast console screen.
2-10 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
2
This section describes the commands for entering your own program from the keyboard
and listing it back again on the screen or on the printer, and other commands for saving
your program on a disk file and loading it back again. The summary below provides a
brief synopsis of each command:
After you type the ENTER command and press carriage RETURN, MegaBasic presents the
first line number and waits for you to type a program line. After you finish typing the
line and terminating it with a carriage return, MegaBasic gives the next line number in
the sequence and you enter another line, and so on. To terminate the process, type a
CTRL-C or ESC at any point or a carriage return immediately after the automatic line
number appears. Since the last line you entered is always in the editing buffer, you can
use editing controls to use all or part of that line in constructing the current line,
potentially saving a significant amount of work.
You can backspace over the automatically generated line number and change it into any
number you desire. After you enter a line into the program, the next automatic line
number will be the number just entered plus the step-size specified by the ENTER command.
You can edit the current line number to re-direct the sequence of automatic line numbers
during program entry without typing additional ENTER commands.
If the automatically generated line number matches a program line that already exists,
MegaBasic displays its contents, positions the cursor on the first non-blank character
after the line number and lets you edit the line using the editing control keys described
in Chapter 1, Section 6. At that point you can edit the line, skip it by typing a carriage
RETURN, or get out altogether by typing CTRL-C. If you edit or skip the line, MegaBasic
resumes the ENTER process with the next line number in the series. To correct
previously entered lines without leaving the ENTER mode, type a CTRL-K during the
ENTER process to go back to the line preceding the one you are on.
MegaBasic does not perform any syntax checks on lines you enter into a program.
However, if you forget the closing quote on a string constant, MegaBasic automatically
adds one to the end of the line. Since this can potentially enclose unwanted characters
within the string (e.g., subsequent statements on the same line), MegaBasic provides a
warning message to indicate this action. Also, MegaBasic removes trailing semicolons
from any line that you enter.
LIST is extremely flexible because of the many combinations possible. The following
examples illustrate possible LIST commands along with a description of what they do.
2-12 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
2
MegaBasic uses the optional string argument to search through the line range given and
list only those lines containing that string. You can include question marks (?) in the
search string to act as wild card characters that match any character (see the information
about this feature under the EDIT command in Chapter 2, Section 1). Upper and lower
case letters match as the same letters.
You only need to specify the <device> number to direct the program listing to an output
device other than the console (device #0). Usually this would be the printer (device #1),
but may also be an opened file number. The resulting file contains pure text suitable for
subsequent LOADing and also for processing by other text file utilities (e.g., text editors
and formatters) which cannot handle the coded format of normal MegaBasic program
files. See Chapter 7, Section 1 for further details about text file processing.
Carriage Return
Typing a carriage return all by itself in the command mode has a special purpose. It will
display a block of program source which precedes the last line LISTed, edited, entered
or interrupted during program execution. For example if your program encounters an
error and aborts with one the various built-in error messages, you can immediately view
the region of the error by typing a carriage return. Likewise, if you have been editing a
group of lines for a while, you can view your work by getting out of the edit mode
(using CTRL-C or ESC) and typing a carriage return. Typing additional carriage returns
will display successive lines that follow the initial group displayed.
To determine how many lines to display, MegaBasic scans backwards through the source
from the current line back to a line beginning with a REMark or preceded by a line-feed,
up to a maximum of 12 lines. In this manner, you can view the most recent logical group
of lines at the touch of a button (RETURN key). When MegaBasic reports an execution
error message with a line number, it sets the current line to that line number. This
permits rapid review of the source region leading up to the error.
Writes the current program onto the file specified. If the file
SAVEfilename name extension is .PGM then you need not type it. You
must specify the file name extension if it is not a .PGM file.
Writes the current program onto the same file that it was
most recently LOADed from or SAVEd to. MegaBasic
SAVE
supplies a default name of UNNAMED.pgm if you have
not yet assigned a name to the program.
Writes the current program onto the same file name, but to
SAVEd: the specified drive. Notice the colon after the drive letter,
indicating the letter is a drive code, not a file name.
Writes the current program onto the same file name of a
SAVE
d:\path\ differentdirectory and/or drive. Notice that the pathname
ends in a slash to indicate it is a directory, not a file.
Saves the program on the file specified by the complete file
SAVE
pathname given. You can specify pathnames in any form
d:\path;\file allowed by the operating system.
Regardless of how you specify the SAVE command or what workspace contents you are
saving, MegaBasic asks you to confirm your request with a yes/no response after
displaying the entire file name and indicating whether or not the file already exists in the
2-14 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
2
file directory. Answering N (for no) aborts all further SAVE action. Answering Y (for
yes) saves the program to the file indicated, which is automatically created if not already
present.
When there are modified programs in other workspaces and you give a SAVE command
without specifying any file name, MegaBasic asks you if you want to save all modified
workspaces. Answering no (N) causes the usual SAVE of the current workspace.
Answering yes (Y) causes MegaBasic to sequence through each unsaved, modified
workspace while letting you confirm or deny a SAVE on each one. This automatic SAVE
option is only requested if other workspaces containing modified programs are present.
To SAVE an unmodified program, you have to specify a file name in the SAVE command;
SAVE by itself does nothing if no programs in memory have been modified.
The SAVE command detects when another user (in a multi-user operating system or
network) has modified a program file you are about to SAVE and issues a warning that
you are about to overwrite their changes. You are then given the opportunity to abort
the SAVE or go ahead with it. This check is performed ONLY when you are saving to
the file from which you LOADed the program, i.e., a SAVE with no arguments that uses
the previous LOAD name.
Files written with the SAVE command are exact memory images of the program in its
internally encoded form. Therefore other programs such as editors and other text
processing software unaware of the program structure within the file cannot process
MegaBasic program files. Furthermore, earlier Z80 versions of MegaBasic cannot
execute these files as programs. Whenever you SAVE to an existing file, any program
that was loaded and converted from a text or other non-pgm format, MegaBasic informs
you that you are about to write your program in Binary Format, and requests your
confirmation. By answering N (for no), the SAVE is aborted. This extra confirmation is
not requested if the destination file is new.
The MS-DOS and Xenix 386 operating systems organizes files in a hierarchical structure
of files and subdirectories. To access a file, you must therefore specify a path of names
from the top of the hierarchy down to the desired file. MegaBasic supports pathnames
in any form acceptable to the host operating system. For example the file ../x refers to
the file named X in the directory just above the current directory. Consult your
operating system user’s manual for detailed information about how to use and specify
pathnames. You should also read the material in Appendix B, Section 1 of this manual
for some differences between MS-DOS and Xenix regarding the formation of correct file
and directory pathnames.
One word of caution is in order here. There have been various pathname management
utilities for MS-DOS operating system to allow programs which were never designed to
work in the pathname environment to use files in some or all subdirectories. Such
programs may make files in those subdirectories appear as if they exist in the current
directory. This can cause problems with MegaBasic or other programs that have been
properly designed to take full advantage of the pathname environment, as they can be
fooled into thinking that such files really do reside in the current directory. Therefore
avoid such programs when using MegaBasic. Although it may work, MegaBasic is in no
way guaranteed to work in systems that have such programs installed.
present, MegaBasic lets you choose to either replace it with the incoming file, or else
preserve it and load the file into a new workspace. In either case, MegaBasic selects the
receiving workspace as the current workspace and assigns it the name of the file just
LOADed. Using successive LOADS, you can bring into memory, one at a time, up to 64
programs simultaneously, limited of course to the amount of memory actually available
in your machine.
When you specify more than one program file in the LOAD command, they are each
LOADed into separate workspaces from the one you are in, and leaves you in the same
workspace from which you started. The file names must be separated from one another
in the LOAD command with spaces.
Before erasing the contents of a workspace prior to loading another program into it,
MegaBasic looks to see if it contains original work which would be lost. If so, MegaBasic
informs you and gives you the opportunity to abort any further LOAD action. MegaBasic
never lets you destroy original, unsaved work without confirmation.
If the program file is not found in the directory implied in its name, MegaBasic searches
each of the subdirectories specified in the MS-DOS alternate PATH= list maintained by
the operating system, in order to find the program. See Chapter 10 for further details
about the file lookup order.
Wherever the program came from, MegaBasic retains its full drive and pathname so that
any subsequent SAVE commands can write the program back to its original file and
directory no matter what the currently selected directory happens to be.
2-16 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
2
h The text file must not exceed 65535 bytes in length. Attempting to LOAD files longer
than this will result in a Length Error.
h Upper and lower case do not matter, but MegaBasic will impose its own upper/lower
case conventions on the resulting program.
These are all the commands provided by MegaBasic to revise your program by editing
source lines, to make global substitutions, to merge programs from other files into the
current program, and other modifications. The following list summarizes them:
2-18 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
2
The editing process steps from line to line until you have edited the last line of the line
range, or until you type a CTRL-C or ESC, or until you enter a new command. Anything
without a line number is considered a command and therefore if the first key you type
(after a line is presented) is not a digit or editing control character, edit mode exits
automatically and the character becomes the first character of the next command.
Whenever you exit the edit mode while a line is presented on the screen, MegaBasic
erases the line shown before accepting the next command. If you want to bring it back
and continue editing, use F5 or Ctrl-R.
At any time during the EDIT mode you can type a Ctrl-K (up-arrow on some
terminals) to edit the line preceding the current line being shown. Repeated use of
Ctrl-K sequences backward through the program one line at a time. See Chapter 1,
Section 6 for a complete explanation of this key. To get the most out of the EDIT process,
you should understand the material presented in Chapter 1, Section 6.
If you specify the <search string>, MegaBasic will present only those program lines
(within the given line range) that contain the string specified. Upper and lower case
letters in the search string are equivalent. You must enclose the string with quotes (““ or
”) if it contains any commas, significant leading or trailing blanks, or it begins with a
period (.), digit or dollar sign ($). When MegaBasic finds a line that contains a <search
string>, the entire line is displayed and the cursor is positioned in the line where the
<search string> was found. At that point you can edit the line or skip it by typing a
carriage return, after which MegaBasic skips to the next line containing the <search
string>.
For flexibility, your search string may contain special wild card characters that match any
character. This special character, a question mark (?), may appear anywhere in the
search string (except as the first character) and as many times as desired. With this
concept, the string A??= will match all assignment statements with variable identifiers 3
characters long beginning with the letter A. The following examples illustrate and
describe each of the various forms of the EDIT command:
EDIT300-499,read Edit lines containing the string read in the lines numbered
from 300 to 499.
EDIT M???= Edit all lines containing a five character sequence
beginning with M and ending with =.
After editing a line and entering it, MegaBasic automatically presents you with the next
line that follows it in the line number sequence. Because of this, if you edit the line
number the edit will continue from that point in the program. You can re-start the
editing sequence anywhere in the program by simply typing an unused line number at
the desired starting point (followed by a carriage return). This normally deletes the line
there, so be sure that the number you select is not in use.
You can edit an executing program after you interrupt it with a CTRL-C. Afterward,
you can usually continue its execution from where you interrupted it. This may be
desirable when, during debugging your program, you discover a programming error
requiring a small correction. However, there are certain program lines which you cannot
alter without disrupting program continuability. When you edit such a line, MegaBasic
will inform you with the message: Program continuation no longer possible. Consult the
CONT command in Chapter 2, Section 4 for complete information about the effect of
program alteration on execution.
When an error occurs in a running program, MegaBasic places a copy of the line in
which the error occurred into the editing buffer. This lets you immediately examine and
modify the offending line after MegaBasic reports an error in it. MegaBasic does not
automatically display an erroneous line, but you can access it by typing the appropriate
previous line access control keys.
Whenever you enter new or edited lines into a program, MegaBasic does not, in general,
perform any syntax checks on the line. However, there are two corrective actions
MegaBasic takes automatically. If you forget the closing quote (’ or ”) on a string
constant, MegaBasic automatically adds one to the end of the line. Since this can
potentially enclose unwanted characters within the string (e.g., subsequent statements
on the same line), MegaBasic provides a warning message to indicate this action. Also,
since some users have a tendency to place a semicolon (;) at the end of a program line,
MegaBasic removes trailing semicolons from any line that you enter.
2-20 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
2
Specifies the range of lines that you wish to search and change.
<range> When you omit the <range>, CHANGE acts upon the entire
program.
Specifies a string of characters that you want replaced by
another string. Lines which do not contain the search string
<search$>
remain unchanged. Special characters can be used to match
any character or string of characters.
Specifies the string that you want to substitute for each
<replace>
instance of the <search$>.
Specifies zero or more single-character switches that alter the
<opts>
way MegaBasic conducts program line searches.
These arguments are fully described in Chapter 2, Section 1 and you should understand
this material in order to take full advantage of the CHANGE command. When you
include wild-card characters in the <search$>, you should use the verify option to avoid
unintentional replacements. The following examples show various ways you can type
CHANGE commands:
It is wise to use the verify option when you specify a numeric search string in a CHANGE
command. Short numbers can easily occur within longer numbers and unintentional
replacement can cause considerable work to repair. To a lesser degree, unintentional
replacement of sub-strings can occur with any search string, and for that reason you
should be careful using CHANGE. When in doubt about what a search string will match,
you can always try it first in a LIST command to see what matches before changing
your program.
CHANGE is a very general purpose tool that you can apply in a wide variety of situations.
However if you are changing line numbers or renaming user assigned names (e.g.,
variable or function names), you should employ the REN and NAME commands for these
purposes instead of using the CHANGE command. These special purpose tools not only
execute faster, but they perform their specific task automatically and completely. For
example, when REN changes a line number, it also changes all references to that line
number, wherever they may be throughout the program (search strings do not even
access the line number portion of a program line). The NAME command can rename a
variable I to J without changing all the other I’s to J’s that are not variables (e.g., in
remarks or string constants).
You can append an additional parameter string to a CHANGE command, called option
switches, to enable or disable various additional search capabilities. This optional
The important thing to understand from these examples is how to manipulate text by
specifying only its surrounding context. You should try out these techniques on some
practice source programs (without saving the results) to get a good feel for how they
work. When appropriate, any-string substitutions can replace many hours of editing
with a few minutes of effort.
2-22 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
2
You can type any combination of selectors in any order after the NAME keyword,
separated from one another by spaces. MegaBasic displays only those names that satisfy
all the selectors specified, for example:
NAME SHARED $ Displays all string functions in use by the current program which some
FUNC package has defined as SHARED.
NAME DIM $ Displays the names of all string arrayvariables.
NAME: Displays all the line labels in the program.
NAME $ NOT FUNC Displays any string name not a function and not dimensioned
DIM (i.e., simple string variables).
NAME NOT :
Displays all names except strings and line labels.
STRING
NAMEINTEGER
Displays the names of all integer functions
FUNC
NAMEINTEGER
Displays the names of all integer arrays.
DIM
NAMESHARED Displays names of all floating point variables and functions which are
REAL currently declared SHARED.
The NAME command depends on the current data type defined by each name. To obtain
this information, MegaBasic processes all DEF statements in when you type the NAME
command. If there are syntax errors in any DEF statements then MegaBasic aborts the
NAME command and reports the error found. Also, the names of arrays and the names
of SHARED objects defined in external packages will not be shown unless you have
executed the ACCESS statements that bind the SHARED names to their references. NAME
provides a count of the names it displays after listing them.
2-24 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
2
All arguments are optional, but you have to omit them from right to left. The following
examples illustrate how you might apply the REN command:
MegaBasic always validates the implied operation that you request and aborts with an
Out Of Bounds Error to prevent overlapping line ranges or illegal line numbers.
MegaBasic always properly updates references to renumbered lines throughout the
program. Line number references to nonexistent lines remain unchanged. The resulting
range of lines affected by any REN command will become the .. current line range (see
Chapter 2, Section 1).
MOVE is like the REN command but without any line increment step size. The following
examples illustrate the variety of ways to type MOVE commands:
MOVE Move the entire program so that its first line starts at line
number 100.
MOVE 4000 Move the entire program so that its first line begins at
4000.
MOVE 335, 450 Move line 450 to line number 335.
Move all program lines numbered 500 and up so that
MOVE 800, 500-$ the first of these begins at line 800. This form is
particularlyuseful for opening up holes in the line
number space for a new block of program lines.
MOVE 900, 300-399 Move lines in the 300 range to the 900 range.
MegaBasic validates the implied operation that you request and aborts with an Out Of
Bounds Error to prevent overlapping line ranges or illegal line numbers. MegaBasic
properly updates references throughout the program to line numbers that have moved.
Line number references to non-existent lines remain unchanged. After a MOVE
command, the .. current line range, discussed in Chapter 2, Section 1, is the set of lines
moved.
This is the starting line number where you want the new
block of lines to begin. MegaBasic assumes line number 10
<starting line>
for this argument if you leave it off. Omitting it also implies
that you have also omitted the other arguments as well.
This specifies the increment or spacing between the copied
lines. It defaults to 10 if you omit it. The step size must be 1
<step size>
or greater, and it cannot be so large that it forces the last line
number beyond 65535. MegaBasic traps both of these errors.
This specifies the block of lines that you wish to copy, as
they are numbered before the copy. When you omit this
<line range> argument, MegaBasic copies the entire program to the
<starting line> specified. See the complete discussion on
specifying line ranges in Chapter 2, Section 1.
COPY works just like renumber, except that the original lines remains unchanged and a
renumbered copy appears elsewhere in the program. The following examples illustrate
the various forms of COPY:
MegaBasic always validates the implied operation that you request and aborts with an
Out Of Bounds Error to prevent overlapping line ranges or illegal line numbers.
MegaBasic properly updates all line number references throughout the program to both
the original and the copied lines. Line number references to non-existent lines remain
unchanged.
2-26 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
2
DUPL is like the COPY command without the line increment argument. The examples
below illustrate a variety of DUPL commands:
DUPL Copies the entire program into line 100 with the same
inter-line step sizes.
MegaBasic always validates the implied operation that you request and aborts with an
Out Of Bounds Error to prevent overlapping line ranges or illegal line numbers.
MegaBasic properly updates all line number references throughout the program to both
the original and the duplicate lines. Line number references to non-existent lines will
remain unchanged.
from one another with a comma and each specification can take any of the following
forms:
<start> Merges all source lines into the target program start-
ing at the line number specified.
<fr om>-<to> Merges source lines from the range specified into the
same line numbers of the target program.
Merges the source lines of the named subroutine
<subr name> (i.e., a function or procedure name) into the same
line numbers of the target.
<start>:<fr om>-<to> Merges source lines from the range specified into the
target program at the starting line numberspecified.
Merges all source lines at or above the line specified
<start>:<fr om>- into the target program at the starting line number
specified. The dash (-) is optional.
Merges the source lines of the named subroutine into
<start>:<subr name> the target program at the starting line number speci-
fied.
For example, MERGE PROG 100:500-699, 200:SORT, CALC merges lines 500 to 699
from program PROG into the current program at line 100, all lines of subroutine SORT
into line 200 and all lines of subroutine CALC into the same line numbers they already
have.
The <start> line number actually renumbers the incoming program lines so that they
begin on the line number you specified. MegaBasic accomplishes this by adding the
appropriate constant value to each and every line number and line reference so that the
beginning line number comes out as desired. This renumbering process affects neither
the current program nor the contents of the source program file you are merging.
MegaBasic does not proceed with any merge that would lead to line numbers greater
than 65535 and reports such a case as an Out Of Bounds Error.
Perhaps the most useful capability is the <subr name> specification, which lets you
merge procedures and functions from program files or other workspaces directly into
your program by name. When you specify this symbolic line range, MERGE searches the
source program for a procedure or function by that name. If found, the effective line
range specified consists of all lines of the subroutine starting with the initial DEF
statement, along with any immediately preceding REMark lines, up to and including the
line containing its terminating FUNC END or PROC END statement. Failure to find the
named subroutine in this manner terminates MERGE with an appropriate error message.
REMarks that follow subroutines are not included. Incorrectly formed subroutines (e.g.,
missing FUNC/PROC ENDS) or errors encountered in other DEF statements along the way
may also terminate MERGE.
Since you can specify multiple ranges and errors do terminate MERGE operation,
MegaBasic describes each range as it is being merged so that you can tell how far it
progressed if an error does occur. Each source/destination specification is processed and
completed from left to right as specified in the command and any error encountered will
immediately terminate further MERGE processing.
2-28 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
2
you in the process of building a new program by pulling in blocks of code from other
programs .
By preceding any <start>:<source> specification with a plus sign (+), MegaBasic
searches for an available target range beginning at or above the <start> specified large
enough to hold the <source> lines. The target destination will always be a block of line
numbers beginning on a multiple of 100. If no <start> was specified, the search begins at
line 1000. An Out Of Bounds Error occurs if no available target region could be found.
This section describes the MegaBasic commands that you use to run your program, test
for bugs, stop and examine some variables, continue where you left off with
single-stepping, resume until some condition becomes TRUE or FALSE, etc. The flexible
debugging environment provided for controlling and monitoring program execution
reduces the effort needed to fully develop software, so any extra effort you spend to
master these commands will quickly pay off with more productive testing and
debugging sessions. The list below summarizes the execution and debugging
commands:
Ctrl-C Aborts the execution of any program in progress and puts you
into the command level. The program may be re-started later
again later.
Re-starts a program that you previously interrupted with a
CTRL-C, or one that interrupted itself with a programmed
CONT STOP. Continuation is possible even if you have modified the
program, changed variable values, saved it on a file, or performed
virtually any other command operation.
Selects various options that show the progress and current state
of program execution at the program source level as execution
TRACE proceeds. TRACE provides many dif ferent options and controls
for selective display and conditional invocation of execution trac-
ing. TRACE modes are set or reset on a workspace by workspace
basis.
Quick check of the program in the current workspace for
CHECK common syntax errors like wrong line numbers, improperly
formed loops, unbalanced parentheses, etc. CHECK reports all
errors at once.
2-30 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
2
For instance you can interrupt a running program and display the contents of an array
before continuing. Or you may want to use MegaBasic as an intelligent calculator by
displaying complex numerical expression values. Direct execution is an important tool
for debugging programs, but you can also enter any statements directly simply to
experiment and learn about them. The following example illustrates how you might
display an entire text file on the screen with one direct statement:
Open #8,“TEXT”;
While input(8); Input #8,L$; Print L$; Next
Direct FOR, WHILE and REPEAT loops execute properly only when you enter the entire
loop as one line. Direct expressions may access any built-in or user-defined functions,
GOSUBS and procedures at any time. However if there are any syntax errors in a DEF
statement anywhere in the program, MegaBasic reports them for you to correct before
you can execute any direct statement. This is because MegaBasic performs a local
initialization of your program DEFinitions prior to executing direct statements.
GOTOs cause a CONTinuation (see above) followed by a branch to the line number
specified. A direct RETURN also CONTinues program execution, followed by a RETURN
from the current subroutine level (unless the program was not CONTinuable within a
subroutine). You can alter the contents of program variables, and such alterations carry
over to CONTinued execution.
Before executing a direct statement, MegaBasic scans your entire program for DEF
statements, so that it can satisfy any potential references in the direct statement to
user-defined function and procedure. MegaBasic reports any errors uncovered during
this process, and if there were any, terminates without executing the direct statement.
Therefore, don’t be surprised if an error message with a line number appears after you
enter a perfectly correct direct statement that doesn’t even use any functions or
procedures. Because of this DEFinition scan, you can type new user-defined functions
and procedures into your program and then immediately proceed to use them in direct
statements without ever running the program. This is especially useful for quick testing
of new definitions.
test this extraction process from MegaBasic, you can type the argument sequence in a
RUN command (e.g., RUN ARG1 ARG2). When the program begins, this string will reside
in the edit buffer for subsequent access. This technique is useful for passing file names to
programs and for using MegaBasic programs in batch files.
Which ever workspace you are in when you give the RUN command becomes the main
program. Prior to beginning program execution, RUN performs the following sequence
of operations:
h The program residing in the currently selected workspace becomes the main
program. RUN erases all data currently defined by the main program all initialized
variable storage to free space. A No Program Error results when you type the RUN
and the current workspace is empty.
h RUN marks all temporary workspaces as free, and releases any data they own. This
consists of all unaltered packages brought into memory with INCLUDE or ACCESS
statements.
h RUN preserves all packages that you LOADed into memory along with any local data
they currently have defined, and it severs all ACCESS relationships between them
and the current workspace. RUN preserves the data defined by such packages so
that special purpose packages can remain available indefinitely (e.g., debugging
routines or completely independent programs).
h The main program is set to permanent status (regardless of its prior status). The DEF
statements throughout the program are all initialized and then the program begins
execution.
A thorough understanding of the material presented in Chapter 10 is necessary for
effective development, testing and debugging of programs spanning more than one
workspace.
Ctrl-C
This is not a command, but a control key used for stopping whatever process is currently
underway: a sort of a panic button. When CTRL-C is struck during execution of a
program, it STOPS program execution like a STOP statement, but can be trapped like an
error. This is useful during the debugging phase to see where execution is currently
happening or to immediately terminate an erroneous program.
When a program stops for any reason (i.e., END, STOP, errors), MegaBasic selects the
workspace of the program containing the line in which the stop took place. This is most
convenient for debugging purposes and eliminates the need for explicitly selecting
packages (via USE) in many instances. MegaBasic displays the current package name at
the Ready prompt whenever it differs from the one selected at the last Ready prompt.
Your program can trap a CTRL-C interruption using an ERRSET statement (Chapter 6,
Section 4) as a type 15 error. This provides a programmed response to a CTRL-C, instead
of interrupting execution. Also, the PARAM(1) statement (Chapter 9, Section 5) can
enable/disable the CTRL-C apparatus during program execution. Since the CTRL-C
detection mechanism consumes all keyboard characters typed during execution,
disabling CTRL-C is useful for both preventing user intervention and permitting
one-at-a-time console character input.
2-32 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
2
When you type a CTRL-C at the MegaBasic command level, it aborts the current entry
or command and then gives the Ready message, instead the STOP message. MegaBasic
generates the STOP message to indicate the interruption of a running program. The
CTRL-C break character provides this terminating effect only when you type it from the
console keyboard. It is just another control character when entered from any other
device. PARAM(1) also lets you use the CTRL-Break mechanism of MS-DOS to interrupt
program execution without consuming input typed during execution.
CONT
Resumes program execution after a CTRL-C or programmed STOP. Error information
functions (e.g., ERRLINE, ERRMSGS, etc.) are not restored and subsequently relate to the
CTRL-C instead of to some prior error. Between the STOP and a subsequent CONTinue,
you can execute direct statements without losing the ability to CONTinue. You can
access variables and OPEN files with direct statements while in the command level.
Regardless of what package workspace you are in when you type CONT, MegaBasic
always switches to the workspace in which the STOP took place, prior to resuming
execution.
You can also modify the program source to some degree without losing the ability to
CONTinue execution. This is powerful during the test and debugging phase of your
program development. You can insert new program lines to temporarily show certain
intermediate values and computations; you can locate and correct programming errors
then re-test them all during one run of the program.
CONTinuability can be lost if you modify certain key program lines. This includes
program lines that called GOSUBS, procedures or functions that are still active, the
beginning of loops and the line on which execution was interrupted. You can always
determine such lines by using the TRACE RET command, which displays the entire active
RETURN path. CONTinuation is also lost when a REN, MOVE, DUPL or COPY command
changes the sequence of any program lines. MegaBasic informs you that
CONTinuation was lost following any action on your part which blocks
CONTinuation. However it is always safe to insert additional lines into the program
without ever affecting CONTinuability.
When your program is in a CONTinuable state, you can cause a continuation by typing
one of several executable direct statements, instead of the CONT command. These
statements are given below along with a description of what they do:
GOTO
CONTinues execution at the program line specified by the GOTO statement
(e.g., GOTO 150).
CONTinues at the first statement following the most recent GOSUB or
PROCedure call. If no such calls are currently active, an UnexpectedRETURN Error
RETURN
results. If your program is suspended inside a function, instead of a GOSUB or
procedure, you must also supply a RETURN value to avoid causing an error.
NEXT
CONTinues at the first statement following the current FOR, WHILE or REPEAT
loop. If no loops are currently active, an Unexpected Next Error results.
2-34 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
2
You can use CTRL-C to enter the command mode, execute commands or examine
program variables using direct statements, and then resume using the CONTinue
command. However, several TRACE function keys provide convenient, immediate
information about the state of your running application. These are described below:
Trace InformationKeys
Displaysthe active program control structures—same the TRACE RET display
A
(Chapter 2, Section 4), except that the current DATA read pointer location is also shown.
Displays the set of currently open files, including their names, open modes, file sizes and
F
current file positions- same as the SHOW OPEN command display (Chapter 2, Section 5).
Displaysthe names, sizes and other statistics for all the present workspaces—same as the
S
SHOW command display described in Chapter 2, Section 5.
Displays the contents of each variable that appears in the current TRACE line. Both the
name of the variable and its contents are shown. It displays strings in quotes with unprint-
V able characters shown as underscores. It evaluates subscript and indexing expressionsas
needed to access array or string elements. Since this invokes user defined functions in such
expressions, global function side-effects may affect subsequent program operation.
When you type V in the TRACE mode, MegaBasic evaluates each variable shown on the
line and displays it. However, you need to be careful about array or indexed string
variables that contain extended assignments or user-defined functions. For example,
consider the following statement:
X = ARRAY(l,let J+=1)
Every time that ARRAY( ) is evaluated, its J subscript is incremented. This occurs both
during execution and when TRACEd with the V option. Since this modifies the program
execution state, the V option in such a case can and will interfere with subsequent
program execution. Likewise, references to user-defined functions needed to resolve
variable accesses can also change the execution state and interfere with later execution
(i.e., by modifying global variables).
In general, this difficulty cannot be detected and handled by MegaBasic. The only
defense against its potential interference with your program execution state is being
aware of the pitfalls and avoiding the TRACE V option when you know it can lead to
trouble.
2-36 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
2
TRACERET
This command displays the RETURN path active at the time that the program stopped
(e.g., CTRL-C, STOP, program error, etc.). The first line number shown will be the point
at which the program stopped. The RETURN path goes all the way back to the first
subroutine call made from the main program.
MegaBasic describes each RETURN location with the type of RETURN (GOSUB, function,
procedure, etc.) and the line number and subroutine name to which it RETURNs. Each
description is shown on a separate line and since this can potentially be quite lengthy,
you can use any of the display-pause controls available under the LIST command (i.e.,
Space-bar, carriage RETURN, CTRL-C). TRACE RET operates only in the command mode
and has no effect on the dynamic TRACE mode if set.
In addition to the subroutine RETURN information, TRACE RET also displays all active
FOR, WHILE and REPEAT loops and CASE statement blocks, along with the line number
range they span, and all the nested ERRSET error traps levels that have been set along
the way. If you suspect that a loop is not terminated where you think it should be, you
can STOP the program inside the loop (e.g., by inserting a STOP statement inside it) and
then give the TRACE RET command to show its current active line number range.
You can also get the TRACE RET display from the single-step mode (i.e., without entering
the command mode) by typing the TRACE A key.
TRACEEND
Terminates the TRACE mode from the command level. You can also terminate the
TRACE by typing the ESC key when you are single-stepping. Program execution proceeds
without further interruption after turning off the TRACE and CONTinue execution.
2-38 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
2
h CHECK reports all errors found throughout the program all at once, regardless of
how many that might be (except for DEFS). However, it reports only the first error
of several on a line; you must correct the first error on the line before CHECK will
report any other errors it contains. You can control the display of long lists of error
messages with the pause-step-start keys as defined for the LIST command. You
cannot restrict the CHECK operation to only a partial set of program lines.
You can redirect the error report to any device or open file by specifying the open
file/devicenumber (#1 is the printer). Omitting the device number is the same as
specifying #0, which outputs to the console screen. You can type a CTRL-C to abort the
error report at any time, and you can control the report output using the same pause
keys that the LIST command supports.
The CHECK command does not perform a complete, exhaustive analysis of program
syntax, but merely locates some of the more obvious errors in program formation.
Because of its simplicity, CHECK can report errors that may not actually exist, particularly
bracketed [1 constructs that span multiple lines (resulting in a mistaken missing or
unexpected bracket error). Bear in mind many program constructs cannot be verified
without actually executing them within their program context, hence a 100% syntax
checker is beyond the scope of the MegaBasic development system. For an exhaustive
100% CHECK on your program, compile it under the MegaBasic compiler which also
verifies all data types and argument LISTs as well. Remember to that errors can easily
occur within syntactically perfect programs, a problem that all programmers must
contend with when using any language.
Described in this section are the informative commands that provide displays of useful
information on your programs in memory, statistics about current resource utilization,
state of program execution and current environment. It also discusses how to be in
several copies of MegaBasic simultaneously, and how to get out of MegaBasic when
finished with everything you are using it for. A quick summary follows below:
Exits MegaBasic and goes back to the host operating system command level. BYE also
BYE exits a nested BASIC environment (see the BASIC command below) and goes
back to the prior environment.
Displays a variety of useful information about your program, its execution state, and
STAT the supporting resources maintained for general use.
Displays all currently defined workspaces by name along with information about their
SHOW content. It can also show the shared access relationships between the current work
space and all the others, information about all currently open files and sizes of
currently defined arrays and strings.
Selects other workspaces by name for subsequent operations and creates new work-
USE spaces for program entry. It can also continuously cycle through all workspace
names so that you can select one without having to type its name.
Displaysa cross-reference index report for the program contained in current
XREF workspace.
Deletes the current workspace and its contents, or only the variables currently defined.
CLEAR It can optionally delete all workspaces or release all memory in use by program vari-
ables.
Enters an independent nested environment for developing, testing, debugging or run-
BASIC
ning other programs while temporarily suspending the current work underway.
BYE
Terminates MegaBasic and exits back to the operating system command level. Prior to
exiting, MegaBasic will request confirmation for any workspaces containing original
work that you have not yet saved on a file. If you previously invoked a BASIC
command (see below) BYE will exit from that instance of MegaBasic and RETURN to the
prior copy of MegaBasic. BYE is equivalent to DOS without any arguments.
STAT [#<device>]
Displays various sizes, states and other statistics about the current program and working
environment. The display is divided into two groups. First the overall global resources
are shown, which are then followed by statistics about the current program and
execution state. Display contents may change from one MegaBasic version to the next,
but they will generally cover the following topics:
h Overall number of memory bytes allocated to current processes Total memory
remaining, including memory allocated to freed packages Amount of space
remaining for evaluating expressions Total number of active named objects
(variables, functions, etc.) File buffer counts and space remaining on the default
drive
h Current workspace name and workspace count (if more,than one) Size of the
current program and size of its data (if any) Various statistics about the current
execution state States of various debugging and internal parameter settings.
2-40 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
2
SHOW [#<device>]
The SHOW command displays a one line description of the contents in each workspace.
Each description includes the program name, the workspace contents type, the package
execution usage, package access count, the program size and how much data it currently
has initialized. The possible workspace types and execution usages are described below:
Packages that are initialized with data and active for use, but are not accessible from the
main program, directly or indirectly (i.e., not on any ACCESS path from the main
program), are classified as Detached. This helps you see packages that are left floating
without any apparent use, but that otherwise remain active and initialized, consuming
memory until they are DISMISSed from all packages. For example, if two Detached
packages ACCESS each other, their epilogues will not be executed until some other
package DISMISSes them from each other. This is because epilogues are not executed
until all ACCESSes to them have been DISMISSed. Packages that are merely
INCLUDED, rather than ACCESSed, are another example of a Detached package.
Because of the potentially large number of packages that applications may keep in
memory, SHOW commands display the package names in alphabetically sorted order.
The currently selected workspace is marked with an arrow (>) in front of the name.
To assist debugging efforts, the SHOW listing places an asterisk (*) beside the program
type of programs with TRACE mode active.
relationships with other packages are not shown. You can specify an optional output
device number to redirect the output. The ACCESS statement is described in Chapter 10.
Space, Sequences forward to the next workspace name in ascending alphabeticalorder. After
→ or ↓ the last one, it cycles back to the first workspace name again.
Backsp, Sequences backward to the next workspace name in descending alphabeticalorder.
← or ↑ After the first one, it cycles back to the last workspace name again.
Home Goes to the lowest workspace name in sequence.
End Goes to the highest workspace name in sequence.
Sequences forward to the next workspace name that was explicitly LOADed
Tab
(i.e., skipping packages temporarily loaded by the applications).
Character Skips to the next workspace whose name begins with the specified character.
Enter Selects the workspace name currently shown as the current workspace.
Ctrl-C Aborts the USE command without changing workspaces.
or ESC
When you specify a workspace name in the USE command, MegaBasic looks for it
among the current workspaces defined. If it finds it among those present in memory,
MegaBasic selects that workspace. If it does not find it, MegaBasic creates a new
workspace with the name given, subject to user confirmation, then selects it as the
current workspace.
2-42 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
2
Specifies the device number to which the cross-reference listing is sent. If you
omit the <device>, then XREF displays its report on the console. If the device
<device>
specified is not the console (device #0), MegaBasic inserts page breaks into the
XREF report using form-feed characters (an ASCII 12) at appropriate places.
Specifies an optional line range to restrict the XREF report to only names and line
<line range> numbers referenced at least once within the line range. This tells you where all
references to anything within this range are found throughout the program.
Omitting the <line range> implies the entire program.
Specifies a list of attributes that selects the kinds of objects that MegaBasic
<selectors> includes in the cross, reference listing. The selector list is identical to the
selector list used in the NAMES command described on Chapter 2, Section 3.
Selects the how the references will be shown: by line shows line numbers, by name
by <mode> shows subroutine names. When omitted, XREF defaults to the most recent
<mode> specified or to by line if no previous XREF requested.
XREF reports on the program maintained in the current workspace. Hence to generate
an XREF report, you must first LOAD your program into a workspace. Then type XREF
followed by any desired device number and/or line range and terminate with a carriage
return. XREF immediately begins generating its report as specified. To pause the display
(especially on the console screen), you can use any of the stop/start/step controls.
You can use XREF from the MegaBasic command level at any time. It does not use any
working storage, nor does it affect the contents of variables for a temporarily suspended
(CONTinuable) program. This makes it suitable for use even during a debugging
session.
You can restrict the cross-reference produced by XREF to names having certain specified
attributes. This lets you produce a cross-reference listing of, for example, only the
procedures, or only the string functions, or only the line labels and line numbers, to
quickly answer questions about your program under development without having to
XREF the entire program, which could take a while for a large program. You do this by
listing the desired attributes as arguments to the XREF command in the same manner as
in the NAMES command explained in Chapter 2, Section 3 . For example, XREF FUNC
will cross-reference all the user-defined function names; XREF STRING FUNC will
cross-reference only those functions which return a string result. These attribute
selectors control which of the following 12 breakdowns are included in the listing:
You can type any combination of selectors, separated by spaces, in any order after the
XREF keyword. XREF displays only those names that satisfy all the selectors specified.
See the discussion in the manual on the NAMES command for complete information
about how to use and specify attribute selectors.
XREF assumes the data type currently defined for each name, which may not always be
accurate if the program has not been run. For example, it shows references to SHARED
functions and procedures defined in other packages as variables unless prior program
execution has already defined them and bound them to the references in the current
program. Although fields appearing in DEF STRUCT statements are always shown in
XREF LISTings, those defined in regular executable STRUCT statements are shown as
ordinary variables unless those STRUCT statements have been executed (i.e., by running
your program before your XREF listing).
Finally, you can display the references either by line number or by subroutine name, by
appending either BY LINE or BY NAME to any XREF command. If you omit the
BY-suffix, XREF displays in the same mode it did the last time you entered an XREF
command, or by-line if no previous XREF command was typed. For example the
command XREF INTEGER BY NAME displays the names of subroutines that refer to each
integer variable or integer function. Program references that are not within procedures
or functions (i.e.,they are in the main program, the prologue or epilogue, or in between
subroutines) are shown in the XREF display as referenced by <main>. Multiple
references to a name within the same subroutine or line always show up in the XREF
listing as a single reference.
CLEAR
Deletes the program within the current workspace and then eliminates the workspace
altogether (unless it is the sole workspace). Afterward, MegaBasic switches to the next
workspace in the LOAD sequence. MegaBasic asks you if you want to clear all
workspaces or just the current workspace, to which you can answer yes or no.
CLEARDATA
Deletes all data currently defined within the selected workspace (variables, control
structures, etc.) and releases the memory resources allocated for them back for reuse.
Program CONTinuation is not possible after invoking this command. You can use
2-44 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
2
CLEAR DATA to prepare for a series of direct statements which are known to require
more memory resources than otherwise available. The RUN command does an implicit
CLEAR DATA at program initialization time, as does the LOAD command if there is not
sufficient memory left to load a program.
CLEARFREE
Deletes every program that is no longer in use. The SHOW command displays such
programs marked as FREE. MegaBasic normally deletes these programs only when it
needs the memory they occupy for other operations. Hence the only real reason for
using CLEAR FREE is to eliminate the extra clutter brought about by unneeded
workspaces left by prior program testing. CLEAR FREE is extremely conservative about
what it deletes, and, in particular, it will never remove anything containing unsaved
revisions or alter the execution state of a program in progress.
GFK-0256 3-1
3
Numbers are fundamental to all computer applications. Even applications that appear
non-numeric, such as graphics and text processing and language translation, are
intensely arithmetic beneath the surface. Computers have evolved beyond their early
dedication to engineering and equation solving, into tools of creativity and thought
expansion, and yet they still thrive best in the medium of numbers and arithmetic.
When we attempt to classify numerical applications, an important distinction can be
made between applications involving whole numbers, i.e., numbers without any
decimals, and those applications where fractional quantities arise. For example, counting
applications usually involve only the whole numbers, while scientific and financial
applications are built upon fractions of time, dollars or other physical units. This
distinction is important because microcomputers can deal with whole numbers much
more efficiently than with fractional quantities. Hence, MegaBasic supports two
different internal representations of numbers, one exclusively for whole numbers, called
integer representation, and one for general numeric values (including whole numbers
and fractions) called floating point (or real number) representation.
All programs could be written with floating point representation exclusively. However, if
a program spends much of its effort performing essentially integer arithmetic, its
performance could significantly benefit by utilizing the more efficient integer
representation and operations wherever possible. The primary reason for supporting
integers in a computer language is that integer arithmetic is much faster than floating
point arithmetic and integer values use less memory. In the paragraphs that follow, we
will examine the strengths and weaknesses of both number representations (real and
integer), as well as how and why to choose one form over the other.
3-2 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
3
values in array subscripts and string indexing (described later), or other places where
integer representation would suffice. If you do, there is no harm, but your program will
simply run more slowly than it could have with the proper integer declarations and
definitions within your program.
Many programs written using a BASIC that supports only floating point representation
usually contain portions which would run much faster using integers. You can usually
improve the performance of such programs by modifying them to take full advantage of
integer representation without much work. Appendix D, Section 4 contains a step-by-step
procedure that you can follow to convert such programs.
Integer Representation
MegaBasic can represent whole numbers in 32-bit binary integer representation as well as
in floating point representation. Integer representation of numbers is important in three
ways. First, integer arithmetic is many times faster than identical arithmetic performed
in floating point operations. Second, since the vast majority of numeric applications
require binary integers for loop counters, array subscripts and indexed string locations,
using numbers already represented in binary form eliminates the time consuming job of
converting floating point representation into binary representation (which MegaBasic
does automatically).
Third, integer representation is physically more compact than floating point. Integers
require four bytes per value while floating point requires 8 bytes per value (although it
varies from 5 to 10 bytes depending on the floating point precision). This allows larger
integer arrays with the same memory requirements as smaller floating point arrays.
Also, since most programs spend a great deal of time just moving numbers from one
place to another, a more compact numeric representation can also increase program
performance.
MegaBasic integers are more powerful than integers of many other microcomputer
languages because of its internal representation. MegaBasic represents integers
internally in what’s known as a 32-bit twos-complement signed integer, while some
systems use only a 16-bit version of the same thing. This will represent exactly all
integers in the range from minus 2,147,483,648 up to plus 2,147,483,647, instead of
–32768 to +32767 with only 16 bits. With integers of this size, many applications which
would normally have to use floating point can easily use MegaBasic integers. For
example integers can represent dollar figures up to $21 million exactly with MegaBasic
integers (in pennies instead of dollar units).
Virtually all programming systems terminate with a fatal error whenever an integer
calculation produces, even temporarily, an integer value beyond the range of values that
the prevailing integer format can represent. Although the large range of 32-bit integers
diminishes this problem somewhat, it can still arise. MegaBasic solves this problem by
automatically detecting integer overflows while performing integer calculations and
then converting the integers to floating point to complete the intended operation. The
burden of detecting and recovering from this type of error is not a concern of the
programmer, since MegaBasic handles it automatically. Integer overflows in MegaBasic
can only occur when you try to use a value larger than a valid integer when only an
integer will suffice. For example, attempting to store such a value into an integer variable
will result in a numeric overflow error simply because it is not possible. This automatic
recovery from integer overflow is only supported by the MegaBasic interpreter; it is not
feasible to support under the MegaBasic compiler.
3-4 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
3
Numeric constants are the most obvious way to express numbers. Examples are: –1,
5675261, 4.536, 0, –11.111, 00934.2, etc. Constants may be signed or unsigned, but
MegaBasic treats constants like 1,435 as two separate numbers. The smallest numeric
value permitted in MegaBasic is 10^–63 using BCD and 10^–307 using IEEE floating
point versions. Arithmetic operations producing smaller numbers than this always result
in zero (i.e., underflow produces a zero result). Constants must not contain any spaces or
commas within them: because such characters are used to separate numbers, they
would break a constant into multiple constants.
Numeric Notation
To specify ordinary decimal and integer constants, simply type their values with
whatever signs, digits and decimal are appropriate to the number desired. You can
include more than on sign in front of a number, but this is always redundant. For
example the following constants all have the same value: 99, +99, –99, +–+99. If the
number of digits exceeds the floating point precision, then MegaBasic rounds the value
to the nearest value fitting that precision. See the discussion about precision earlier in
Chapter 3, Section 1 for further details about this.
Numeric constants may have, at most, one decimal point. As stated earlier, no spaces,
commas or other non-numeric characters can appear within numbers. However, you can
precede or follow any constant with one or more spaces for the purpose of improving
readability or for separating the constant from other surrounding typed objects.
Exponential Notation
You can also specify numbers in so-called E-notation. Similar to scientific notation, this
format includes a scaling factor to indicate a power of ten multiplier. For example,
23.4104E–2 and .234104 are identical values with the first in E-notation. The E-XX
portion of the number specifies how far and what direction to shift the decimal place (+
for right and – for left). This representation becomes important when you specify
extremely large or small constants. For example the constants –.20152E+42 and
3.3142E–19 would be too unwieldy and confusing with all the zeros needed to represent
them in standard notation.
Whatever the exponent portion of the constant is, the net magnitude of the number
must fall within the dynamic range for floating point numbers: lE–63 to lE+63 for BCD
and lE–307 to lE+307 for IEEE floating point. Constants smaller than the lower limit
evaluate to zero, while MegaBasic rejects constants beyond the upper limit and reports
an Out Of Bounds Error. If the exponent is a positive power of ten, the plus sign (+) is
optional. For example the constants 25E+17 and 25E17 are identical.
3-6 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
3
Program Constants
Constants within programs represent fixed quantities for use in computations.
MegaBasic stores program constants in an internal table for fast access by your program.
If you specify the constant in E-notation, or it contains a decimal point, or it is too large
to fit integer representation, MegaBasic considers it a real (floating point) constant and
physically stores it in floating point representation exclusively.
MegaBasic stores constants in both floating point and integer representations if you
specified them as integers (i.e., no decimal points or E-notation) and they lie in the range
of 32-bit signed integers. This lets MegaBasic choose the most appropriate numeric
representation for the context of each expression providing the fastest possible access lo
the constant. For example MegaBasic accesses the constant 7243.0 exclusively in floating
point mode regardless of its surrounding context, but typed as 7243 (without the
decimal), MegaBasic accesses it as an integer or a real depending on the numeric context.
Input Constants
You can enter constants from the keyboard in response to requests from the computer as
directed by the program. When MegaBasic INPUTS constants into floating point variables,
you can enter any number representable in MegaBasic floating point. However if you
specify integer variables in an INPUT statement, you must enter numbers without any
decimals to the right of the decimal point and they must fall within the range of 32-bit
signed integer representation. The INPUT statement (Chapter 7, Section 1) checks for this
and rejects constants that are inappropriate for the variable specified to receive the value.
Names may be any length up to 250 characters and all characters are necessary to
identify the name, i.e., two names must match exactly in order to refer to the same
numeric variable. You cannot use MegaBasic reserved words (e.g., FOR, IF, READ, etc.)
as variable names. Upper and lower case letters always mean the same thing, and
MegaBasic displays letters in user-assigned names in upper case only. Chapter 1,
Section 5 discusses the use and construction of names in detail.
You can use numeric variable names in any context where one would normally specify a
number. Access to named objects in MegaBasic is extremely rapid and the length of a
name has no effect on execution speed or program size, no matter how many times the
name appears in the program source. There are several different kinds of numeric
variables and this manual refers to numeric variables as scalar variables, simple variables,
or just variables to distinguish them from array variables described later in Chapter 3,
Section 4.
If you access a variable before storing any value into it (by a READ or assignment
statement), it will automatically contain the value of zero (0.00). However, you should
always explicitly initialize all variables before using them to promote clear program
structure and maintainability over time.
3-8 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
3
Another type of numeric variable is the array, which stores an ordered set of numbers
under one name. MegaBasic organizes an array as an ordered set of storage locations,
called elements, identified by a position number within the ordering. For example A(0),
A(1) and A(2) represent the first three elements of array A(). The parentheses indicate
that A() is an array and serve to contain the position of the desired element. Positions
range from zero by integers up to the size of the array. Arrays, as with all other
MegaBasic named objects, must have unique names. You name arrays and assign them
integer or real data types under the same rules as scalar variables.
You could imagine the array described above as a column of numbers with positions
numbered from zero down the side. Suppose that you had many such columns side by
side and that you numbered them from zero along the top. Such a structure is called a
2-dimensional array. By identifying the row and the column we can locate any element
of the group. For example A(I,J) refers to the element of A() in row (I) of column a),
where I,J are simple variables containing the element position. By adding further levels
to this idea, 3 or higher dimension arrays can exist. An N-dimensional array requires N
position numbers, called subscripts, to uniquely specify an element in the array.
You can specify array subscripts as simple constants, variables or with any general
numeric expression. If the specified subscript is a non-integer value, MegaBasic will
truncate it (not round) to the next lower integer value. Real (floating point) subscripts are
internally converted into integer representation before they can be used to access the
array. There is a significant performance advantage in specifying array subscripts using
integer representation whenever possible, because MegaBasic performs no time
consuming real-to-integer conversions. This is especially true in arrays of two or more
dimensions and in compiled programs.
3-10 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
3
2-dimensional array example above. However you can DIMension the array again at
any time to change ib size or number of dimensions. If you do, MegaBasic erases it prior
contents and sets every element to zero. Using this mechanism, arrays can grow or
shrink depending on your program requirements. When arrays are made smaller the
unused memory space is available to the system for other uses.
Since the dimensions of arrays can vary during the execution of your program,
determining the current dimensions of a given array can be useful from time to time.
The DIM() built-in function (see p. 425) provides such information for any variable.
DIM(x) gives the number of dimensions of the variable X; DIM(X,I) gives the highest
position defined for dimension I of variable X counting the dimensions from left to right.
Default Arrays
For compatibility with other BASICS, whenever your program accesses an array element
from an array that does not yet exist, MegaBasic automatically creates small
1-dimensional array. MegaBasic normally creates such arrays, known as default arrays,
with 11 elements numbered 0 to 10, equivalent to DIMx910). You can change this
default upper bound (of 10) by setting PARAM(13) to any upper bound value from 0 to
1023 of your choice.
We strongly recommend that you do not write programs that rely upon default arrays
because this practice often complicates the test and debugging phase of developing such
programs. For example, by merely misspelling the name of an array in some reference to
it, MegaBasic will create a default array of that name if one does not already exist by that
name. You can turn off default array creation by setting PARAM(13) to a value of –1.
Any subsequent references to new, unDIMensioned arrays will cause an Undeclared
String Or Array Error, helpful in locating unintentional default array creations.
performance and other reasons, several additional restrictions apply to arrays larger
than 65534 total elements:
h Pointers to array elements can only access the first 65534 elements. Pointers to
arrays (rather than to array elements) are unrestricted.
h The value returned by INDEX after a vector MIN/MAX will wrap around through 0
if the result exceeds 65535.
h Vectors longer than 65535 elements cannot be indexed. Arrays larger than this
can be indexed, but only as array slices whose length does not exceed 65535
elements.
h Array slices whose successive elements are more than 65535 physical elements
apart are not permitted. For example, DIM ARR(10,100,1000) may be sliced as
ARR(*,*,J) or ARR(I,*,*), but not as ARR(*,I~ because its successive elements
are over 100,000 elements apart.
Obviously, you must have enough memory to support whatever arrays you actually
dimension, which tops out around 540k in a 640k DOS machine. Protected-mode
versions of MegaBasic, such as Extended MegaBasic, have no 640k limitation and
support massive arrays of up to 16 megabytes.
3-12 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
3
When you re-dimension an array, the array type (integer or real) always changes to the
type specified by the DIM statement (e.g., DIM INTEGER x(); DIM REAL x(); etc.). If the
DIM statement omits the word REAL or INTEGER, the array assumes the numeric type
already in effect by the prior DIM or DEF statement. Only the MegaBasic interpreter lets
you change array types during execution; the compiler insists that array keep the same
type throughout program execution.
You will find further details about type declarations in Chapter 3, Section 1 (a few pages
back) and Chapter 5, Section 1 describes the DEF INTEGER and DEF REAL statements.
Chapter 3, Section 1 also describes important differences and properties of both real and
integer representations that you should be aware of.
Operation Precedence
MegaBasic evaluates expressions by proceeding left to right, accumulating the result
with each operation as it goes. The various operators are not however applied with
equal priority. Take the following expression using addition (+) and multiplication (*) for
example:
2 * 3 + 7 * 8 evaluates as:
(2*3) + (7*8) = 6 + 56 = 62
A generally accepted practice of algebraic evaluation is that, in the absence of parentheses,
we should perform the multiplications before the additions. Hence we say that
multiplication takes precedence over addition. Similarly, MegaBasic applies a priority scale
to all operators to provide a reasonable order of operations that appear without
parentheses. However, you can force any order of evaluation as needed by surrounding a
sub-expression with parentheses (sub-expressions have the highest prior and take
precedence over all operators). For example, to evaluate the addition in the example
before the multiplications, just write it like this:
2 * (3 + 7) * 8 evaluates as:
2 * 10 * 8 = 20 * 8 = 160
The list below summarizes all the MegaBasic numeric operators in order of decreasing
precedence. When MegaBasic encounters operators of the same precedence level they
are evaluated from left to right.
3-14 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
3
Operators on the same line have equal precedence and MegaBasic evaluates them from
left to right as encountered. An example of an expression involving only equal
precedence operators is:
X + Y – 3456.03 + ARRAY(J).
MegaBasic permits you to use either integer or real values wherever a number is
expected. Internally, MegaBasic usually operates on only one type or the other for any
particular operation and if you specify values in the wrong type, MegaBasic will convert
them to the right type. Since this conversion operation is somewhat time consuming,
your programs will run much faster if numeric values are always supplied in the form
(integer or real) most suited to the operation at hand. The descriptions of each operator
follow below and include the specific rules MegaBasic uses for integer and real
conversion.
Arithmetic Operators
The arithmetic operators are the most familiar and simplest to describe. The left and
right operands around an arithmetic operator are simply combined algebraically into a
result value using the specified operation. MegaBasic includes the following operators:
These operators process only operands of the same type (i.e., both integer or both real).
If they differ in type, MegaBasic automatically converts one of them to the type of the
other, and the operation continues. The result of such an operation is always the same as
it would have been if performed in floating point only. Integer comparisons are faster
than floating point comparisons.
The divide operation (/) first converts any integer operands to real so that a floating
point divide can then yield a floating point quotient. To do an integer divide, you must
use the DIV operator (e.g., I DIV J) and supply two integer operands. When either or
both operand is real, the DIV operator performs a real DIV operation and truncate the
final quotient to an integer in real representation. DIV always performs an integer divide
when both operands are integer, resulting in an integer quotient.
3-16 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
3
MegaBasic supports seven operators providing bit-wise logical and shifting operations
on 32-bit integers: four logical operators for implementing bit-wise NOT, AND, OR and
XOR, and three shift/rotation operators. These operators have an operator precedence just
below that of arithmetic plus and minus, and above the MIN and MAX operators. Real
operands are always converted to 32-bit integers before the operation is applied and
every result is a 32-bit integer. All seven operators are summarized below in order of
decreasing relative precedence:
The magnitude of the shift and rotate counts has no effect on execution time, as these
operations are performed in one step rather than a bit at a time. The shift and rotate
operators have equal precedence (i.e., below – and above &).
Computes the value of A with the sign of B. The result value always has
A SGN B the same numeric type as the left operand (integer or real). For
example: 38 SGN –5 = –38, 38 SGN 5 = 38, –38 SGN –5 = –38,
38 SGN 5 = 38.
Selects the MINimum or MAXimum value between the two operands,
A MIN B for example: 2.3 MIN–34.7 = –34.7, 23456 MAX 45 = 2~456. This is
A MAX B faster than the more general MIN() and MAX() functions, which
also set the INDEX function value as a side-effect.
Computes the closest multiple of B to A. This is equivalent to the
A ROUND B expression: ROUND(X/Y)*Y. For example: 135.4592 ROUND .1 =
135.5, 53474 ROUND 50 = 53450.
Computes the lowest multiple of B equal to or greater than A. This is
A CEIL B equivalent to the expression: CEIL(X/Y)*Y. For example 354 CEIL
25 = 375.
A INT B Computes the highest multiple of B equal to or less than A. This is equiva-
lent to the expression: INT(X/Y)*Y, for example 354 INT 25 is 350.
Computes the nearest multiple of B between A and zero. This is
A TRUNC B equivalent to the expression TRUNC(X/Y)*Y, for example 27
TRUNC 5 is 25, –27 TRUNC 5 is –25.
The last four operators (i.e., ROUND, CEIL,INT and TRUNC) are the so-called multiple
reduction operators, which reduce a value to a nearby multiple of another number. As in
the other arithmetic operators, MegaBasic automatically forces their operands to the
same type before the computation begins and produces a result of the same type. Also,
faster execution results when you can supply integer operands.
3-18 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
3
Logical Operators
MegaBasic provides logical operators to manipulate logic and evaluate logical
expressions, but they are unusual in that they do not use the full numeric value of their
operands. Instead, MegaBasic uses only the zero or non-zero characteristic of their value
instead of their whole value. Think of this property in terms of TRUE and FALSE, with
TRUE being non-zero and FALSE being zero.
The result of a logical operation is always an integer zero (0) or one (1) and reflects the
combination of two logical values into one logical result. NOT reverses the logical value
that follows it, i.e., NOT FALSE is TRUE (1) and NOT TRUE is FALSE (0). Notice that NOT
only has one operand, similar to the negation operator (–). Operands of logical
operators may have an integer or real type, but MegaBasic converts logical operands to
an integer 0 or 1 before before evaluating the logical operator. Each of the logical
operators are described in the table below:
A useful way to understand logical operations is to list all possible logical inputs (i.e., the
operands) alongside their corresponding outputs (i.e., the results). This is usually quite
easy to do with logical operations because logic only deals with two values: true and false,
but not at all practical with real or integer operations because of the enormous number of
combinations. Such enumerations with logical values are called truth tables, an important
tool in applied logic. A truth table providing a complete definition of all MegaBasic
logical operators now follows:
3-20 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
3
However if VALUE equals 0 (false), then the final result is known to be false, so
MegaBasic skips over the complicated right operand without having to evaluate it. As
this example shows, a many-fold speed improvement may result when the right
operand requires much more computation than the left, and the left operand is false.
In case (b), MegaBasic evaluates the expression from left to right and as soon it
encounters a true term, further evaluation is unnecessary. This is because true ORed with
anything is still true. When connecting terms with the OR operator, you can make it more
efficient if you arrange the terms so that the term most likely to be true is first, the next
most likely true term is second, and so on. You can optimize a similar AND-sequence by
ordering the terms in a similar manner (i.e., most likely false value first).
Case (c) is a combination of cases (a) and (b). The ORed sub-expression to the right of the
AND is only evaluated if TEST equals LIMIT (on the left). However when evaluating it,
MegaBasic proceeds only until encountering a true term (among VAL1 or VAL2 or VAL3).
MegaBasic applies these optimizing identities at all levels of expression evaluation, no
matter how complex the expression.
Do not assume that this optimization is always performed, because different
implementations may or may not do it (e.g., the compiler does evaluate things
differently). We mention it here so that you can order your operands for the most
efficient processing and so that you do not depend on the right operand necessarily
being evaluated. Nor should you depend on the lack of evaluation of a right-operand,
even if the above conditions are met. Right-hand operands that affect the contents of
variables or other program-state conditions must be coded with the knowledge that they
may or may not need to be evaluated.
Remember that the result of a logical operation is an integer result, never a real result.
You might want to consider the possible performance consequences of this depending
on the context. However such consequences only affect execution speed and are
logically transparent to the particular application involved.
When you employ logical operators for numerical purposes you must be aware of the
operator precedence involved, or you could easily produce meaningless results.
Although this is the case with expressions of any type, the range of operators in
MegaBasic is greater than is generally supported in most other languages. Therefore you
should experiment with unfamiliar operators in simple expressions to understand them
before applying them in complex situations.
Comparison Operators
Comparison operators compare two numbers (integer, real or mixed) or two strings and
pass back the outcome of the comparison. You can compare an integer value with a real
value, but MegaBasic automatically forces them to the same type internally before
actually comparing them. A Type Error occurs if you attempt to compare a number with a
string. When you perform a comparison, you are looking to see if some relationship
between the numbers is true or false. For example you may want to test whether one
number is equal to another number. The equality comparison returns true if they are
equal and false if not equal.
By convention, MegaBasic (like most other computer languages) represents logical
values with numbers: 1 means true and 0 means false, and represents such values in
integer format rather than in floating point. Logical values (true and false) are primarily
found in IF statements and WHILE or REPEAT loops to decide what the next step of the
program should be. Based upon the outcome of a comparison, your program can choose
one set of actions over another. However, logical operators can also be used within
arithmetic computations for their 0 or 1 value whenever desired. For example the
statement: COUNT=COUNT + (X>Y) adds 1 to COUNT only if X is greater than Y. All the
comparison operators are described in the table below:
A=B Returns a true if A and B are exactly equal, and false otherwise.
A<B Returns true if A is less than (below) B, and false otherwise.
A>B Returns true if A is greater than (above) B, and false otherwise.
A <= B Returns true if A is less than or equal to (not above) B, and false otherwise.
A >= B Returns true if A is greater than or equal to (not below) B, and false
otherwise.
A <> B Returns true if A and B are not exactly equal, and false otherwise.
A IN B Returns true if all 1-bits in A are also set to 1 in B, and false otherwise.
You can compare two expressions results just as you compare simple values. The
operator precedence scale becomes important in such comparisons to reduce the need to
control operation order with parentheses in expressions involving many diverse
operators. The following expression illustrates such a calculation:
A + B * C > X * Y ^ Z OR Q – R / S = A * B AND F *17 < B + C
You can greatly improve the readability of expressions like this by carefully
inserting/deleting spaces between operators to make them stand out and by grouping
the operations with parentheses, as in:
(A+B*C)>(X*Y^Z) OR (Q–R/S=A*B) AND (F*17<B+C)
Since string comparisons also return 1 for TRUE and 0 for FALSE, you can use them
within larger numeric expressions as needed. For example the expression I + A$=SB$
computes the value I+1 if A$ and B$ are identical, or the value I+0 if they are not. Refer
to Chapter 4, Section 4 for details on how MegaBasic compares strings.
3-22 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
3
First, we have the square-root of 17. Second, we specified a value equal to the contents of
X rounded to exactly three significant digits. Third, we access a value equal to the
minimum value specified among the expressions: R+2, 189, and VALUE. Functions are
always of the same form:
<function name> (<argument list>)
Input information to the function is specified after its name, enclosed in parentheses, as
a list of numeric or string values called an argument list. Each input value is called an
argument and is specified using any general expression. The values computed by these
expressions are used by the function in forming its ultimate result. The number of
arguments and their type (string or numeric) depends on the particular function being
used. When more than one argument is present, they are separated from each other
with commas. Some functions have no arguments, and are specified with the function
name alone: no parentheses follow it.
MegaBasic possesses a library of over eighty built-in functions and also allows you to
create your own functions, written in MegaBasic statements. Chapter 9 provides a
complete description of all the built-in functions in MegaBasic and how to use them.
Defining your own functions is a somewhat more advanced topic that is thoroughly
covered in Chapter 8, Section 3. Refer to those subsections for more complete details. A
summary of the numeric functions now follows for quick reference.
Because variables are created by default when encountered for the first time and not
DIMensionded, misspelled function names will result in variables being created under
those names. Such errors can be very difficult to diagnose because there is no way for
MegaBasic to detect the error. For example, SQRT(I) returns the square-root of I, and
SQR(I) returns the Ith element of array SQR().
Two facilities exist in MegaBasic to aid the discovery of misspelled names. One is the
NAMES commands (Chapter 2, Section 3), which displays an alphabetical list of all
user-assigned names in the program. Unrecognized names that appear in this display
should be investigated. Mistyped variable and function names tend to be displayed in
close proximity to the correct spelling of the user-assigned name, due to the alphabetical
ordering of this display. The second debugging aid is the XREF command (Chapter 2,
3-24 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
3
Section 5), which displays all references to any name. Names that have only one
reference should be scrutinized as possible misspellings.
Vector Variables
Several statements and functions are supported that provide a complete vector
processing facility in MegaBasic. To effectively use these constructs, you need to
understand how to specify vector variables and vector expressions. A vector variable is
defined as:
h Any numeric scalar variable or single array element reference. This is the
shortest possible vector: a vector of length one.
h An array name without any subscript expression, representing a vector that
consists of all elements contained in the array, even if the array has more than
one dimension. In multi-dimensional arrays, the element order is the same as
the traversal by the following program:
Dim ARRAY(L,M,N)
For l = O to L; For J = O to M; For K– O to N
Print ARRAY(I,J,K); Next K; Next J; Next I
In other words, we advance a subscript only after sequencing through all possible
combinations of all the subscripts to the right of it. This type of vector variable lets you
process any array as if it were one long list of numbers.
h An array slice, representing a vector consisting of all elements of the array that
intersect with a slice through one or more dimensions of the array. Array slices
are described below.
h A concatenated vector variable, which is a list of vector variables separated by
commas and surrounded by brackets. This type of vector expression is discussed
later on.
3-26 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
3
Scalar variables or arrays that have never been DIMensioned or created by default
cannot be referenced as a vector. Attempts to use such uninitialized variables in a vector
context will be reported as Out Of Context Errors.
Concatenated Vectors
As mentioned earlier, a vector variable can be specified as the concatenation of two or
more vectors, by enclosing the component vectors in brackets []. For example [X,Y,Z] is a
vector with three vector variables, forming a sequence of numbers consisting of the
three vectors placed end to end. The component vectors within brackets may have any
of the following forms:
h A scalar variable
h An array slice or an indexed array slice
h An unsubscripted array name
h A scalar expression that does not begin with a user-defined identifier
All components of a concatenated vector must have identical data types. In other words,
all components must be integers or all components must be floating point (real). A Data
Type Error is reported if this rule is violated.
The last form lets you specify 1-element vector components using ordinary arithmetic
expressions. For MegaBasic to discriminate between such an expression and a vector
variable, the expression cannot begin with an identifier (e.g., you can surround scalar
expressions with parentheses). Hence scalar expressions considered valid include any
expression that begins with a numeric constant, a left parenthesis or a built-in MegaBasic
function. Components specified in this manner represent read-only values. If you store
data into a concatenated vector, any read-only components that it contains will be
treated as variables and modified accordingly. No error is reported for this condition;
you simply lose whatever value is stored there.
Extended (or compound) index expressions are also fully supported, i.e., indexing an
already indexed vector. Note that you cannot index an array without any explicit asterisk
subscripts because MegaBasic assumes that the first parenthetical expression that follows
an array name must be a subscript expression, not an indexing expression. Unlike
indexed characters strings, indexed vectors must select at least one element; a null vector
is not allowed.
Vector Expressions
Computations involving vectors are expressed in much the same way as ordinary scalar
calculations. For example if X and Y are vectors, the expression (X+Y)/2 will produce a
result vector whose elements are the average of the corresponding elements in X and Y.
There is virtually no limit on expression complexity or parenthesis depth, and the
internal memory required during the computation is only slightly greater than that
required for a similar scalar computation.
Vector expressions are evaluated completely for the first element of every term of the
expression, followed by the second element of every term throughout the expression,
and so on through to the last element of each term. If the vectors of an expression differ
in length, then the shorter vectors will run out before the longer vectors are accessed.
When this happens, the shorter vectors simply wrap-around back to their first element
again, so that the expression computation can continue until the last element of the
longest vector has been processed.
For example the expression X+3 is the sum of a vector X and a constant. A constant is
really a vector of length one, so that when we evaluate this expression the constant
becomes, in effect, a constant vector of length equal to the length of X. Some other
3-28 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
3
important applications of this wrap-around idea will be shown later on, but for now the
important thing to understand is that a vector expression always produces a vector
result equal in length to its longest vector variable. You can control any wrap-around by
controlling the lengths of the vectors involved.
Vector Operators
All the standard MegaBasic arithmetic operators are supported in vector expressions.
Mixed-mode (i.e., integer and real) arithmetic is supported under the same rules as in
scalar arithmetic, including operator precedence relationships. Unlike scalar arithmetic, if
the result of an integer calculation exceeds the capacity of a 32-bit integer, a numeric
overflow error is reported, instead of converting the integer to floating point and
continuing on. If an error occurs during a vector computation, you can determine on
which element the error occurred using the INDEX function, which always returns the
number of correctly computed vector result elements.
All the logical operators (i.e., AND, OR, XOR, EQV, IMP and NOT) and all the comparison
operators are supported. As in the scalar context, they return an integer 0 or 1 result, or
rather, a vector of 0’s and 1’s. Using these, the expression SUM(X>=10 and XC=30), for
example, computes the number of elements in vector X that lie in the range from 10 to
30.
Vector Functions
All arithmetic functions (Chapter 9, Section 1) and mathematical functions (Chapter 9,
Section 2) are supported in a vector context, with the following exceptions:
Pi is supported as well as INDEX, which begins at zero and increments by one as the
vector expression sequences from element to element. INDEX can be used in a vector
expression as a running counter-vector, or after an error (such as divide by zero) to
determine the element that caused the error during the vector computation. The
dramatic speed improvement of vector operations over iterative implementation may be
reduced when transcendental functions are applied to vectors, simply because such
operations are dominated by computation.
None of the file and device l/O functions are supported, nor are the utility and system
functions, except for INTEGER() and REAL(). When you apply a function to a vector
expression, each element of the expression result vector is transformed by the function
to produce a new vector of transformed elements. For example SQRT(X+Y) returns a
vector consisting of the square root of the sum of the corresponding elements in vectors
X and Y. If you attempt to use any MegaBasic function that is not supported in a vector
context, an Out Of Context error will be reported.
Vector Statements
The vector processing statements are simply enhancements of selected scalar processing
statements that already exist in MegaBasic. These include vector assignments and
swapping, printing, and file reads and writes. In each of these statements, you must
indicate that a vector operation is coming up, by preceding the operation with the
special reserved word VEC. We will describe each of the vector statements in the
discussion that follows.
The INDEX function, referred to elsewhere in the discussion, returns the number of
processed elements at any point. It is often useful for setting vectors to an arithmetic
sequence.
Some vector operations can take a while to execute, depending primarily on the number
of elements to be computed and the complexity of the calculation. Heavy use of
transcendental functions on 100,000 elements without a math coprocessor can take quite
some time to complete. During this time, Ctrl-C is not recognized, causing a perceptible
delay between the time you type a Ctrl-C and the time your program stops.
3-30 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
3
Vector Assignments
Virtually all vector processing is performed by vector assignment statements, which
have the form:
VEC <vector variable> = <vector expression>
The reserved word VEC announces a vector assignment is ahead, the <vector variable>
defines where to store the resulting vector, and the <vector expression> defines the
vector calculation to be performed.
The length of the vector variable dictates the extent to which the vector expression is
performed. For example in the vector assignment VEC X = 3, the constant 3 is a
one-element vector which is extended (or repeated) to match the length of vector
variable X. As another example, consider the following program fragment:
Dim X(100), Y(10);
Vec Y = index; Vec X = Y
First create two vectors (arrays), one with 101 elements and one with 11. Then assign the
INDEX function value to each element of Y(*). The INDEX function always returns the
number of successfully computed vector elements from any vector computation.
However within a vector expression, INDEX creates a vector consisting of an
incrementing series of integer values starting with zero.
Finally, we assign vector Y to vector X. Since Y is shorter than X, MegaBasic extends Y to
the length of X by repeatedly wrapping around to the beginning of Y each time it runs
out. This results in X containing 9 concatenate series of integers 0 to 10, finishing with 0
and 1 in elements 100 and 101. This automatic repetition can be useful in matrix
manipulation, as demonstrated by the assignment statement:
Vec M(*,*) = M(*,*) + R(*)
where M(i, *) and R(*J are the same length
If M() and R() have the same number of columns, the statement above adds R(*) to
every row in M(). This implicit repetition means that you must be careful when setting
up vector operations to specify vectors of the appropriate lengths at all times. Just one
element too few or too many can easily produce invalid results that may be difficult to
detect, especially when other vector operations follow.
You also need to use care in applying the automatic repetition to avoid excessive
computation when doing simple things. If, for example, S is a scalar variable and X is a
10,000 element array, the statement VEC X - SQRT(S) would compute and store the
square root of S 10,000 times, a very time consuming and wasteful approach. It is much
more efficient to compute and save a complex result once, then store it into a vector as a
separate step.
A concatenated vector variable (enclosed in brackets [ ] as described earlier) may be the
target variable of a vector assignment, as in:
Vec [A,B,C,D] = X(*,J)
The vector expression result on the right is distributed among the variables on the left as
if they formed one continuous variable, even if any or all of the concatenated variables
are also vectors. If A, B, C and D are scalar variables, then they receive the first four
elements from column j of array X(). As always, the result vector is extended to match
the length of the receiving vector variable, which in this case is the combined length of
the concatenated variables.
When the assigned vector variable also appears in the vector expression to the right of
the equals sign (=), remember that each element is computed and stored one at a time.
In particular, computing one element using the value of another element of the same
vector may not work. Consider the following example:
Vec X(*)(1) = X(*)
This assignment statement appears to assign the values from elements 0 and up to
element positions 1 and up, i.e., shift all element values up by one element. In fact, what
this really does is to copy the value of X(0) to all elements of the vector. This is because
later elements are stored using the results of earlier elements and vector calculations are
always done in ascending sequential order, resulting in the sequence: X(1)=X(0),
X(2)=X(1), X(3)=X(2), and so on. This computational property may be useful in certain
applications but in most cases specifying such assignments causes errors that may be
difficult to diagnose.
With careful application, however, you can take advantage of the sequential nature of
the vector computational process for special purposes. An important example of this is
converting a series of values into a cumulative series. The following program fragment
does just that
Vec X(*)(1) = X(*)(1) + X(*)
This computation first sets X(1) = X(1)+X(0), then sets X(2) = X(2)+X(1), and so on, so
that each resulting element is the sum of itself and all elements preceding it. We can also
convert this cumulative series back to is original incremental series using a similar
technique, as shown by the program fragment:
Vec Y = X; Vec X(*)(1) = Y(*)(1) – X(*)
In this case, we have to copy vector X to another vector so that the elements needed in
the calculation are not modified before they are used. The result is that each new
element X(i) = X(i) – X(i–1).
Swapping Vectors
In matrix applications, one frequently needs to exchange of the contents of two vectors,
such as in matrix transposition and matrix inversion procedures. This generally
time-consuming process can be performed using the SWAP statement which is 7 to 12
times faster than a similar implementation using FOR..NEXT loops. For example, the
following routine transposes an N-by-N matrix using vector swaps:
For l = O to N–1
Swap vec MATRIX(*,I)(I), MATRIX(I,*)(I)
Next I
This routine swaps the contents of each corresponding row and column. As shown
above, the VEC reserved word must precede each pair of variables to be swapped, so as
to distinguish them from other scalar variables or strings to be swapped in the same
statement. As with all vector operations, the INDEX function returns the number of
elements processed after the operation has completed.
When you swap two vectors of different lengths, the process continues until the last
element of the longer vector has been swapped, and the shorter vector is re-started from
the beginning whenever it runs out of elements to swap. For example if you swap a
vector with a scalar (a vector with one element), the scalar is repeatedly swapped with
each element of the vector. The net effect of this is to insert the scalar value into the
3-32 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
3
vector, and move the extra value that falls off the end of the vector into the scalar variable.
A similar insertion occurs when a long vector is swapped with a short vector.
This capability can be useful in vector sorting, vector element rotation, element insertion
and deletion, and other manipulations on numerical arrays. It also means that you must
be careful when you specify a vector swap operation to ensure that the lengths of both
vectors are exactly correct, to avoid an unintended result.
Printing Vectors
You can PRINT the resulting elements of a vector expression by merely specifying the
vector expression, preceded by the reserved word VEC, as any term of a PRINT
statement. Each value is printed with the appropriate format, just as if each element was
specified as a separate (scalar) expression, for example:
Print %“12f2,8i”, Vec X(*,j)
This statement prints all the values from column j of array X() to the console, in a format
that alternates between 12F2 and 8I. This capability eliminates the need for FOR..NEXT
loops for similar applications of PRINT. You can PRINT simple vector variables and vector
expressions of any complexity. The VEC reserved word must precede each vector to be
PRINTed; expressions not marked in this way are assumed to be scalar expressions.
elements are read directly into the vector in one disk operation, instead of a potentially
separate disk read for each element. Separate disk transfers are used whenever the read
involves precision conversions (i.e., PARAM 11 <> PARAM 4), numeric type conversions
(i.e., from READ REAL or READ INTEGER), or non-contiguous elements (e.g., VEC X(*,K),
Y(*,*J), etc.).
When the high-speed vector read is performed, the individual elements are not
validated in any way. Single-element reads employ a very simple validation of each
value read, but this is only of use when the file truly contains garbage. If an actual read
error occurs, such as reading past the end of the file, the INDEX function does NOT
return the number of correctly read elements because INDEX is updated after the disk
read has successfully completed.
3-34 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
3
IEEE/BCD Compatibility
BASIC87 is designed to run programs originally written under BCD MegaBasic without
any program changes. There are, however, a number of areas that you need to be aware
of which can potentially alter the outcome of certain operations. We consider all these
differences to be insignificant in the vast majority of applications. The issues are as
follows:
h When floating point variables are read from or written to data files directly, the
prevailing floating point representation is assumed: BCD versions read/write
BCD, IEEE versions read/write IEEE. PARAM(11) can be set to modify this
behavior, however, as we will discuss later on. IEEE values require the same
amount of memory/file space as floating point values under the 14-digit BCD
version: 8 bytes each.
h Operations that use knowledge of the internal BCD representation will no longer
work correctly. The only way this can occur is by using EXAM and FILL
statements to access memory locations containing floating point numbers, or by
passing the memory address of these numbers to machine code subroutines
outside of MegaBasic. Very few programs will be affected by this incompatibility.
h Results from complex calculations will have small differences in the
least-significant digit or two, so you should not rely on identical full-precision
results for your program to be correct. If you can run your application under
different precisions of BCD MegaBasic, then you should also be able to run it
under IEEE MegaBasic.
h Decimal numbers stored in IEEE floating point variables will not always be
represented exactly, the way they are in BCD variables. This shows up in
FOR..NEXT loops with certain non-integral step sizes, sometimes causing the
loop to execute one less iteration than with BCD floating point numbers.
h The exponents of numbers formatted with E-notation require one more column
of width (for 3 digits instead of 2). This may result in an overflow of the field
width and no value will be shown. If this rare case is encountered, the field is
filled with asterisks (*) and the program continues on.
3-36 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
3
h The relative time to compute most floating point operations will differ under
IEEE and programs relying on such timing may require adjustment. A few
operations are slower under IEEE MegaBasic, such as ROUND(x,n) and
converting between ASCII and floating point, but most operations are much
faster.
h The RND() function generates a completely different sequence of random
numbers under IEEE than under BCD. Results from programs using RND() run
under different versions will not match.
h The TYP() function is not able to reliably distinguish binary data from strings
and end-of-file marks (and never could). Therefore TYP() should not be used to
determine the next data type on files that contain binary data (such as IEEE
floating point, binary integers, etc.).
Values written in smaller precision or read in higher precision are rounded to the smaller
destination precision. Values too small to represent in the target precision are set to zero,
while values too large to represent will cause a numeric overflow error. Values written in
higher precision or read from lower precision values are padded with extra zeros as
needed.
Single-precision IEEE format is provided for applications that need to store
low-precision numbers as efficiently as possible, and for accessing available data files
written in that format. BCD format is supported to allow access to existing data files
written by BCD versions of MegaBasic. A numeric overflow will occur if BCD values
larger than 10^63 are written. BCD MegaBasic does not support the single/double
precision IEEE format.
PARAM(11) normally defaults to the precision/format that the running copy of MegaBasic
uses to represent floating point numbers internally. For example under 14-digit BCD and
IEEE versions, PARAM(11) equals 14 and 2, respectively. However its default value can be
modified permanently for any particular copy of MegaBasic using the CONFlG.pgm
utility.
PARAM(4) returns the numeric precision/format used internally by the running copy of
MegaBasic. Under IEEE versions it returns 2, and for the BCD versions it returns the
BCD precision (as in the above values for PARAM 11).
Software/Hardware Performance
Performance is the real reason for using IEEE floating point representation. BCD
add/subtract operations are very efficient and remain competitive even against the
80X87 processors. However, all other areas of floating point processing exhibit obvious
gains when a math chip is used (e.g., multiply, divide and especially the transcendentals).
The degree of speed improvement you experience will vary with the system clock speed
and math chip type being used (e.g., an 80287 is faster than an 8087,but slower than an
80387). There are several different brands of math chip and their performance varies
widely. Because of this variation, certain internal operations may still run faster in
software than with math chip assistance.
Math chip presence is automatically detected by MegaBasic at start up. If not present, all
operations are performed strictly in software. The use of the math chip can be disabled
or enabled under program control so that you can test your software under both
environments without having to physically remove the chip or run your tests on a
different machine. To enable/ disable the math chip, use the following statements:
PARAM(20) can also be tested to determine if a math chip is being used. It will not,
however, return 1 if it is present, so you need to test it for a non-zero value instead of
one. This is because the value returned by PARAM(20) is a composite value that indicates
the chip type (i.e., 8087, 80287 or 80387) and a measure of relative performance of the
existing chip as compared with the current CPU speed. It is not possible to enable the
chip unless one is actually present in the machine. PARAM(20) under BCD MegaBasic
always returns zero and cannot be set to anything else.
The accuracy of both the hardware and software transcendental functions is very good:
full 16-digit accuracy is maintained for all functions when using the math chip. The
software transcendental functions return results within 15-16 decimals for better than
99% of all arguments supplied. COS() and TAN() return an occasional result good to only
14 digits (for less than 1% of all arguments) . This reduction in accuracy occurs only for
arguments that are far outside the primary function domain (i.e., 0 to 2 pi for
trigonometric functions). In such cases, the argument itself is inherently less accurate, so
the reduced accuracy from the function is not significant.
In order to hide round-off errors in the least-significant digits of displayed floating point
values, numbers displayed in free-form format are shown rounded to 14 digits. You can
use E-notation or other fixed-point formats to see more digits of precision than this if you
need to.
3-38 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
Chapter 4 Representing and Manipulating Strings
4 section level 1 1
figure bi level 1
table_big level 1
Most typical business application programs spend much of their time dealing with
strings: word processing, mailing lists, report generation, command processing, record
processing, and formatting to name a few. Strings can represent binary information, text,
packed numbers or virtually any other data representation. MegaBasic has a carefully
chosen set of operations which when used in combination can efficiently perform all
string operations supported by other high-level computer languages (such as PL/1) with
exceptional string handling facilities. Becoming fluent in MegaBasic string handling
concepts can greatly simplify many of your non-numeric data processing applications.
GFK-0256 4-1
4
By grouping 8 bits together as a unit, we can express 256 values, one for each of the
possible combinations. These 8-bit units, called bytes, are perfect for representing
characters because their 256 possible values is sufficient for assigning a different
value to each letter, each digit, each of the various punctuation marks
(e.g., ?!@#$%^&*()<>,.:“’’’;[]), and still have many left over for special purposes, such
as carriage returns, spaces, form-feeds, etc.
In order for such a character set to really be useful, everyone who uses it must agree on
the same characters for the same 8-bit values. After all, when you print the letter Q on
one device it should also be the letter Q on some other device. Therefore a standard
called the ASCII character set has been assigned to the series of 8-bit values so that
independently developed computing machinery can communicate characters with one
another.
Actually, there are a number of different standard character sets that exist, but ASCII is
the most commonly and widely accepted standard. Appendix D, Section 3 contains a
table of all ASCII characters alongside their corresponding 8-bit values (in binary,
decimal and hexadecimal). The TRAN$() string function (Chapter 9, Section 3) can
convert strings of characters from one character set to another, should the need arise.
Awareness of the ASCII character set is of central importance when you compare strings
with one another using MegaBasic statements. In order to sort strings, for example, you
need to know if one string is less-than, greater-than or equal-to another string. The notion
of above and below depend of the internal values of characters rather than the
characters themselves.
However, individual characters cannot convey very much information. As you read this
sentence, notice that you are not reading one character at a time, but reading words or
even phrases of words as indivisible units of information. Characters are important but
larger chunks of information are much easier to handle, move around and manipulate.
Therefore in a programming language, character information is processed in
multi-character chunks called strings.
A string is a sequence of zero or more characters (8-bit bytes) treated as a single data
object. As with numerical quantities, strings may be expressed as constants, variables,
arrays, functions and string expressions. For example the string constant “This is a String”
is a string with 16 characters. The quotes are used to clearly separate the string
characters from those around it but are not actually part of the string. Without the
quotes, it would be difficult (if not impossible) to tell which characters are in the string
and which characters are outside the string.
4-2 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
4
String constants typed into MegaBasic programs are always delineated using two double
quotes (“...”) or two single quotes (’...’), making it possible to include either quote
character (but not both) within a string constant. You must type quotes around string
constants within MegaBasic programs, but when a string is typed as input to a program
request you never put quotes around it unless the quotes are part of the string itself. It
would be very restrictive and cumbersome if you had to surround all your typed input
with quotes. The following MegaBasic program statement illustrates how you would
print a message on the screen using a string constant:
PRINT “This message goes on the screen without quotes.”
A string can have zero or more characters and although it may seem that a string with zero
characters would have little use, it actually occurs in string applications just like the
number zero occurs in numerical applications. Such an empty string is called a null string.
A null string constant in a MegaBasic program is typed simply as two quotes with no
characters in between (i.e., ““ or ”). Suppose that your program requests string input
from the keyboard and the operator types in nothing. Your program can simply compare
the input received with a null string and take the appropriate action. Remember that
spaces, like those between the words in this paragraph, are not null strings but actual
characters in a string. For example the string constant “ ” is a string consisting of three
characters, all spaces.
If you ever forget to include the terminating quote (“ or ’) at the end of a string constant,
MegaBasic will automatically place one at the end of the line. This can be convenient
when you are typing a string constant as the last item of a line, since the second quote
need not be typed. However, if other terms or statements follow a string constant on the
same line, omitting the final quote causes all following characters to be included as part
of the string constant. Therefore, MegaBasic informs you that it added a missing quote
on programs edited or inserted into the program.
String constants are used in programs to represent fixed character sequences (usually
text) which are manipulated with other strings to form string results. This is analogous to
the use of numeric constants (Chapter 3, Section 2) in programs as fixed quantities. Since
string constants are typed from the keyboard, only the printable subset of ASCII
characters can be placed in them. If you type any control codes (values 0 to 31), the
MegaBasic line editor picks them up and uses them for various editing functions and
they never get into the string constant. However, string constants are not the only way
to express strings, as you will see in the sections that follow.
Character strings from zero to 65502 bytes long may be stored in string variables for later
retrieval by name. A variable name may appear anywhere that string data is acceptable.
By merely referring to any string variable by name, its entire contents are immediately
made available. String variable names must begin with a letter (A-Z), usually end with a
dollar sign ($), and contain any number of intervening letters (A-Z), digits (0-9) or
underscores (_). Names are discussed in-depth in Chapter 1, Section 5. The following
examples illustrate how and how not to spell string variable names:
Using a string variable name wherever string data is expected gives access to the data
stored in the variable. Assigning string data to a string variable replaces its previous
contents with the new string, a process that can be performed by assignment statements
(Chapter 5, Section 2), EXAM statements (Chapter 7, Section 3), INPUT statements
(Chapter 7, Section 1), SWAP statements (Chapter 5, Section 2) and READ statements
(data Chapter 5, Section 1, file Chapter 7, Section 2). For example, the following short
program stores a message into a string variable named LINE$ and then prints the
contents of LINE$ on the screen:
10 LINE$ = “This message is stored in LINE$”
20 PRINT LINE$
Unlike string constants, the characters stored in string variables may assume the full 8-bit
ASCII character code range from 0 to 255. String variables in many computer languages
cannot store the entire range of 8-bit values (0 to 255), but the full range is vital to many
non-text applications. Bit-strings are a typical example of such an application, an
important tool which is described later in this section.
String variables in MegaBasic may be defined to hold any length string up to 65502
characters, as long as the available memory in your machine is sufficient. However since
strings are variable length objects, MegaBasic sets aside a memory area for each string
variable large enough to hold any string up to its defined maximum length. Unless you
explicitly define the maximum size for a new variable, MegaBasic will automatically
assign a maximum size of 80 characters, by default. You may assign your own maximum
string size using a DIMension statement like this:
DIM LINE$(50), BUF$(9999), CHAR$(1)
where LINE$ may store 0 to 50 bytes, BUF$ may store 0 to 9999 bytes and CHAR$ can
store only 1 or 0 bytes. The same DIM statement can define one or more strings by listing
their definitions one after another, separated with commas as shown above. Both string
and numeric (array) variables may appear in the same DIM statement.
Newly DIMensioned strings are filled to their maximum length with spaces (ASCII 32).
This default may be altered at any time to any ASCII code from 0 to 255 using PARAM(7)
in Chapter 9, Section 5. The 80 character default length of undimensioned string
variables may be set to any value from 1 to 4095 using PARAM(12).
4-4 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
4
Although default string variables are convenient, we recommend that you do not write
large programs that rely on them because they are often too large for small string
applications and this practice can complicate the test and debugging phase of
developing such programs. For example, by merely misspelling the name of an string in
some reference to it, MegaBasic will create a default string of that name if one does not
already exist. You can turn off default string creation by setting PARAM(12) to a value of
–1. Any subsequent references to new, unDIMensioned strings will cause an Undeclared
String Or Array Error, helpful in locating unintentional default string creations.
If you want a new string variable to contain zero characters (a null string) from the start,
simply assign a null string to it immediately after you create it, as shown in the example
below:
DIM STRING$(1000); STRING$ = “”
This creates a string variable named STRING$ which is initially set to contain a null
string (), but has the capacity to hold up to 1000 characters.
DIMensioning a string variable already defined re-defines that variable to the new size
specified. Such an operation is useful for releasing unneeded memory back to the system
for further use, and to permit program control over the size of string and array variables.
Since DIMensioning always re-initializes strings (with the default ASCII code), all
previous contents of the variable are lost, as is also the case with numeric arrays.
String variable and function names do not have to end with a dollar sign ($), although
this is a standard practice that makes the data type of string variables, arrays and
functions more obvious when reading the program. Strings names can be declared in the
same manner as numeric variables (integer and real). The complete set of rules and
syntax for declaring names as string entities is presented on the next page. If, however,
you are new to MegaBasic, we recommend that you name all string entities with names
ending with a dollar sign ($) to avoid any additional complication during your initial
efforts in learning MegaBasic. Later on, you can experiment with and take advantage of
the other methods in MegaBasic for declaring string variables, arrays and functions.
4-6 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
4
Another type of string variable is the array, in which a group of string values can be
stored under one name. String arrays are organized as an ordered set of storage
locations, called array elements, that are identified by a position number within the
ordering. For example LINE$(0), LINE$(1) and LINE$(2) represent the first three
string elements of array LINE$. Parentheses are used to indicate that LINE$ is an array
and serve to contain the position of the desired array element. The positions are
sequentially numbered from zero up to the size of the array.
The 1-dimensional array LINE$ above could act as storage for a list of lines of text,
collectively representing a page of text, giving you direct access to each line on the page
by its line (position) number. Suppose that we combine many such pages together into
one string array for access by page (position) number. This is called a 2-dimensional
array. By identifying the line and the page we can directly access any line in the volume.
For example LINE$(PAGE,ROW) refers to line ROW on page PAGE, where PAGE and ROW
are simple variables specifying the array element positions. By adding further levels to
this idea, you can define and access string arrays with 3 or more dimensions. An
N-dimensional array requires N position numbers, called subscripts, to uniquely identify
an element position in the array.
If you specify the wrong number of subscripts in an array reference, as in the last three
examples above, MegaBasic will report an Array Subscript Error. When accessing string
array elements, specify only the array DIMension positions and leave off the length
parameter, which is given only when DIMensioning.
You can re-DIMension the array at any time by re-defining it in another dimension
statement. All stored strings redefined in this manner are erased after such an operation
and re-initialized. Arrays can thus grow or shrink depending on your program
requirements. When arrays are made smaller the unused memory space is available to
the system for other purposes. The following list summarizes some important aspects of
using string arrays:
h An Array Subscript Error occurs if you attempt to access a dimension position
outside its defined range or use the wrong number of subscripts when accessing
it.
h A single DIM statement can define one or more arrays by simply listing their
definitions one after another separated by commas.
h All array elements are initialized the same way as simple strings.
h You cannot assign the same string variable name to both a string array and a
simple string variable. If you create a string array using the name of a simple
string variable that already exists, the simple variable and is contents will be
erased and the specified string array created under the same name.
h All string arrays must be defined explicitly, otherwise MegaBasic thinks they are
simple string variables instead of arrays.
h Array subscripts which are given as fractional quantities are truncated to the
next lower integer value (rather than rounded). For example
BUF$(3.723,0.201) is treated as BUF$(3,0).
h For the best performance, you should employ integer expressions and variables
for array subscripts whenever possible. Floating point variables can be used, but
they will be converted internally to integer representation. Such conversions are
time-consuming by nature and best avoided if possible.
Since the dimensions of arrays can vary during the execution of your program,
determining the current dimensions of a given array can be useful from time to time.
The DIM() built-in function provides such information for any variable Chapter 9,
Section 5). DIM(S$) gives the number of dimensions of the variable S$; DIM(S$,1) gives
the highest position defined for dimension I of variable S$, counting the dimensions from left
to right.
4-8 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
4
MegaBasic supports arrays with any number of elements, as long as no one subscript is
higher than 65534. For example, DIM BUF$(99,99,99,4) dimensions BUF$() to three
dimensions of 100 positions each, or 1,000,000 elements total (6 bytes/element). To
dimension BUF$(1000000,4) is not allowed, because the dimension extends higher than
65534. For performance and other reasons, one restriction applies to string arrays larger
than 65534 total elements: pointers to array elements can only access the first 65534
elements. Pointers to arrays (rather than to array elements) are unrestricted.
Obviously, you must have enough memory to support whatever arrays you actually
dimension, which tops out around 540k in a 640k DOS machine. Protected-mode
versions of MegaBasic, such as Extended MegaBasic, have no 640k limitation and
support massive arrays of up to 16 megabytes.
Strings are manipulated and processed by combining them in phrases called string
expressions, similar to numeric expressions. String expressions permit you to specify a
string as a combination of other strings and are formed from string symbols and string
operations. Although the notation of string expressions looks similar to numeric
expressions, their operation is totally different. The example below combines two strings
together into a string result:
As you can see, the plus sign (+) has a different meaning depending on whether it is
being applied to numbers or to strings. A plus operator used with strings is called a
concatenation operator, because it is used to connect or concatenate two strings into a
longer string.
String symbols used in string expressions include string constants, string variables, string
functions (both user-defined and built-in) and string sub-expressions. A sub-expression is
actually a portion of a larger expression that has been surrounded by parentheses,
grouped as a computational unit.
String operations, called string operators, are of two types: unary and binary. Unary
operators act on a single string to form the result string. For example the NOT operator
preceding a string (e.g., NOT Z$) will produce a result string of the same length but with
each byte logically complemented. Binary operators however act on two strings situated
on either side of the operator to combine them in some fashion producing a result string,
as in the concatenation operator demonstrated above.
MegaBasic evaluates string expressions from left to right accumulating the results from
each operation as it goes. The various string operators are not however applied with
equal priority. Take for example the following string expression involving concatenation
(+) and string repetition (*) factors:
“ABC”*2+“xyz”*3
This expression repeats ABC twice and concatenates it to xyz repeated 3 times (i.e.,
ABCABCxyzxyzxyz). Since the string factors (*) are evaluated before the concatenation,
we say that such factors take precedence over concatenation (just like their numeric
multiplication takes precedence over addition). Similarly, all string operators have been
assigned to a priority scale that controls the order of operations when several
precedence levels are present in the same expression, much like the numeric operator
precedence ordering.
When required, you can override these default priorities by surrounding any operation
by parentheses to force its evaluation in the order of your choice. The example below
illustrates a situation where concatenation (+) is performed prior to a string repetition
factor:
(“ABC” + “xyz”) * 5
4-10 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
4
The concatenation in parentheses is evaluated first, followed by repeating its result five
times. The table below lists the various string operators in order of decreasing
precedence followed by a discussion of each.
This ordering is similar to that of numeric expressions except that strings have some
different operators. You can control the ordering of operations using appropriately
placed parentheses. Be careful using complex string expressions in string comparison
operations. The comparison operators are not really string operators since they produce
a numeric result (i.e., integer 0 for false, 1 for true). They are included in the table above
only to show their precedence within mixed mode expressions. It is the programmer’s
responsibility to ensure that mixed string and numeric expressions are sufficiently
parenthesized to resolve any inherent ambiguities.
String Concatenation
The simplest of the string operations is concatenation (+), which merely appends two
string operands together, end to end, in the order given. For example ABCDE+12345 =
ABCDE12345.
String Subtraction
A$B$ returns A$ with all instances of characters specified by B$ removed, for example:
LEN(A$) – LEN(A$–“ ”)
which simply computes the difference in length between A$ with spaces and A$ with
spaces removed. Of course, this computation can be generalized to count occurrences of
any character or set of characters. Without string subtraction, this computation would
require a programmed loop that checks each character one at a time, taking 20 to 100
times longer.
String Repetition
Any term of a string expression may be repeated by following the term by a multiply
operator (*) and a numeric expression [e.g., ABC*(X+Y)] . First the factor is
evaluated (X+Y), then the string is repeated by that many times. The repetition factor
expression needs parentheses surrounding it only if it contains more than one numeric
term, as in the example above. When only simple factors are used, no parentheses are
required, as in the string expression:
A$*X+B$*37+C$*23.
Many computer languages provide string repetition as a separate function, which is not
nearly as convenient or intuitive as the MegaBasic string multiply. Other computer
languages include a separate function just to generate some fixed number of spaces (“ ”)
in a PRINT statement. To do this in MegaBasic you need only to include a space
multiplied the appropriate number of times (e.g.,“ ”*N) whenever you need it in any
PRINT or other statement.
String expressions are always formed in MegaBasic’s control stack, which can rapidly
overflow when compound repetition factors build up enormous strings that exceed the
available memory space.
This operator is useful for creating masks which may then be used for selective overlay
and wild-card character matching algorithms. MATCH performs a process which would
otherwise require a complicated loop of statements taking far longer to complete. It is
useful to those requiring special assistance in pattern matching applications, and should
be considered an advanced topic. No examples of its use will be given.
4-12 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
4
Both Min and Max carry a precedence just above the Match operator described earlier.
One application for MIN and MAX is character range restriction. For example:
A$ max “ ”*len(A$) will force all control characters in A$ (ASCII 0-31) to spaces. This
expression uses a repetition factor on “ ”, a topic discussed earlier.
The zeros and ones are used only to illustrate bit values, but in actual practice, the
operands and the result are all (bit) strings.
As with numeric expressions, there are many different ways in which to express a given
logical combination. A number of equivalent logical expressions are described below to
further illustrate the logical operators as they are used in actual practice. Given that A$,
B$ and C$ contain bits strings of equal length which will be used as terms in the various
examples below.
Examples (a) to (c) illustrates how you would compute the same result of the XOR, EOV
and IMP operators using only NOT, AND and OR. As you can see, considerable effort is
saved by using XOR, EOV and IMP when their particular computation is required.
Examples (d) and (e) are instances of DeMorgan’s Law, which is a rule for logically
converting ANDS to ORS or ORS to ANDS using NOT. It is useful for reformulating logical
expressions into simpler forms. Example (f) shows how the logical expansions of AND
and OR terms is performed.
As we shall see, bit strings and their associated operations are ideally suited to
applications involving the processing of sets. A set is a collection of related items, such as
the set of all experiments for which we have data, or the set of all employees in a data
base. A given set must define which members are present in the set and which members
are absent. Suppose that we have a set of employee records that can possess up to 1000
members, numbered 0 to 999. Further, let us suppose that we wish to extract some
subsets, like the set of employees which are managers and the set of employees that
earn more than $50,000 per year.
Each of these subsets can be efficiently represented in your program as bit strings, in
which each potential set member is assigned a bit position within a bit string. If the bit
corresponding to a particular member is a one (1), then that member is present in the
set; absent members are similarly marked with zero bits. Letting EMPLY$ be our
4-14 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
4
employee set, MGR$ be the manager set, and RICH$ be the set of employees making
more than $50,000, the following logical string expressions may have some use:
Since strings can be as large as 65502 bytes, you can represent sets with as many as
524016 possible members, one for each bit in the string (given enough memoryand/or
operational stack space). Sets are very general data structures which can be applied in
countless ways, and because the individual set operations are provided in the basic
instruction set of MegaBasic (as the logical string operators), they execute extremely fast.
Many applications in set processing and systems programming work require the ability
to turn bits on (set to 1), turn bits off (reset to 0) and flip their state (change 1s to 0s, 0s to
1s), without affecting the other bits in the bit string. This can is done by applying a bit
selection string called a mask, which controls which bits to change and which bits to leave
unaffected. Given an arbitrary bit string named BITSTR$ and a selector bit string named
MASK$ containing 1’s for selecting bits and 0’s for protecting bits, the following
expressions may be used to selectively alter bit strings:
Other useful applications for bit vector operations include the following conversion from
lower case to upper case. It turns out that if you set bit5 of a character to the logical
combination of (NOT bit6 AND bit5) then the resulting character will be upper case (see an
ASCII code chart to verify this as an exercise). This operation can be performed on an
entire string using the following string assignment statement:
U$ = NOT ROTAT$(L$ AND CHR$(64)~LEN(L$),1) AND L$
where L$ is the original string, U$ is the upper case result string, and LEN(), CHR$() and
ROTAT$() are string functions described in Chapter 9, Section 3. A similar statement may
be implemented to convert from upper case to lower case. If often required within your
program, this is best programmed as a user-defined string function (one-line function).
MegaBasic also includes several string functions designed specifically for bit string
processing. See Chapter 9, Section 3 for complete information on ROTAT$, BIT, ORD and
CARD, which are briefly summarized below:
When strings are compared, the ASCII codes of corresponding characters are compared
from first to last until a difference is detected or the end of either string is encountered.
4-16 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
4
Strings are equal only if all characters are identical and both strings are of equal length. If
one string runs out before a difference is encountered, the longer string is taken as greater
than the shorter string. The following pairs of string constant comparisons illustrate some
of the subtle properties of string comparisons:
Bit-String Comparisons
If your application is using bit-strings, one common operation you may need is a test to
determine if one set is a subset of another set. The IN operator performs this function
which, for the expression A$ IN B$, returns TRUE (I) if every bit position in A$ that
contains a 1 is also a one in each corresponding bit position of B$, and returns FALSE (0)
otherwise. In terms of operations on sets (represented by bit strings), A$ IN B$ tests to
see if the set A$ is a subset of set B$. Like all other string comparisons, IN returns an
integer result, rather than a string, where 1 means true and 0 means false.
If B$ is longer than A$, only the portion of B$ equal in length to A$ is compared. If A$ is
longer than B$, A$ IN B$ can only be true if all the extra characters of A$ have all zero
bits [i.e., bytes containing CHR$(0)] and all the others are IN B$. IN is a bit-string
operation generally used in combination with other bit operations, including: BIT(),
ORD(), CARD(), ROTAT$(), NOT, AND, OR, IMP, EQV and XOR.
4-18 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
4
It is often desirable to access portions of strings, called substrings, rather than the whole
string. Most programming languages implement such access through special functions
like LEFT$(), MID$(), RIGHT$() and SUBSTR$(). MegaBasic uses a different method
to access substrings, called indexing, that is easier to learn, executes faster, requires less
typing and performs the job in a more general fashion.
By convention, we will refer to the left and right ends of a string (oriented horizontally)
as the beginning and end of the string respectively. String indexing is based on the idea
that each character in a string has a position relative to the beginning of the string. We
will assign the first character to be in position 1, the second character in position 2 and so
on to the end of the string. Any portion of a string can therefore be specified by a
position range within the defined positions of the string. For example if A$ is our string
and we wish to access positions 10 through 27, we would express this as follows:
A$(10,27)
As long as the length of A$ is 27 or more, this indexing expression accesses the 18
characters in A$ starting at the one in position 10. If A$ contains less than 27 characters,
MegaBasic will access all characters from position 10 to whatever the length of the string.
A null string results if A$ is less than 10 characters long. Any string constant, string
variable, string function or sub-expression may be the subject of an indexing expression.
Variations on this theme provide several other modes to specify substrings in different
ways having advantages over one another. Each of the string indexing modes is
discussed in the table below. The examples shown in the table use the variable A$ to
represent a general string expression to which the indexing expression is applied.
Given a string A$ (I,J), MegaBasic returns a null string whenever J is less than I (J=0 is
permitted) or I is greater than the length of A$. Also if the substring specified exceeds the
length of the stored string, only that portion which actually exists in the string will be
accessed. For example if A$ contains the string This is a String, then A$(9,1000), A$(9:100)
and A$(9) all refer to the same string: a String. An Out Of Bounds Error occurs if you
specify a starting position less than 1.
Any string or string expression can be indexed, not just string variables. Just type your
index expression in parentheses immediately after the string expression and upon
evaluation, only the indexed substring of the expression result will be returned. Index
expressions have a higher precedence than any of the string operators, hence you must
surround the string expression to be indexed with parentheses if they contain multiple
terms, for example:
(A$ + B$ – C$)(1,J)
Without the parentheses around the expression A$+B$–C$, only the last term of the
expression (C$) would have been indexed. String constants can also be indexed, like any
other strings, and doing so has some important applications. Consider the following
example:
“JanFebMarAprMayJunJulAugSepOctNovDec”(1*3–2:3)
This string expression converts integers 1 to 12 into the corresponding names of the
month (i.e., their names abbreviated to three characters). This process of decoding a
number into some set of keywords or names is frequently required in interactive
software and report generators of all kinds. Indexing a string constant lets you do this in
one simple expression without any string variables or complicated loops to program.
Unlike the month abbreviations above, your keywords may not all be the same length, a
property required by this indexing application. To remedy this apparent deficiency,
insert some padding characters after each of the shorter keywords to force them all to the
same length. Then, index the string constant using that length and remove the padding
characters from the result using string subtraction (Chapter 4, Section 4) or the TRIM$()
function (Chapter 9, Section 3).
This technique depends on all the keywords fitting into a string constant, which must
itself fit within one program line (255 characters maximum). Longer lists of keywords
must be stored in a string variable of sufficient length and indexed in a similar manner.
You can also access longer lists by breaking them into several smaller string constants
that reside on different lines. You would then have to GOTO the appropriate line before
performing the indexed access as described above.
4-20 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
4
are placed left justified into the indexed area, replacing only those characters in positions
required by the incoming string. Consider the following examples:
In the first example, the string constant overlays the string contained in the string
variable starting at position I on up to position J. If that region is too short then only the
left-most portion that fits will be stored. If the region is longer than the assigned string
then the right-most portion of the region itself will not be modified in any way. In the
second example, the INPUT statement can only affect the N positions of B$ starting at
position I. In the third example, the READ statement affects only the last N positions of
C$. It is also important to remember that the indexed region of a string variable includes
only positions that actually contain characters, and excludes any region beyond the
current end of the string (i.e., you cannot alter positions beyond the end of the string or
its length with indexed assignments).
Since the indexed assignment statement above does not fill out the entire indexed region
when the string assigned is too short, another type of string assignment is provided for
this purpose. By using == instead of = any positions to the right of the string that
remain unfilled are set to spaces (i.e., the current string fill character set by Param 7). This
assignment is described in Chapter 5, Section 2.
Another type of string assignment in MegaBasic lets you replace the entire contents of
an indexed region with another string exactly. If the string and the region are different
lengths, MegaBasic automatically shifts the characters that follow the region up or down
to exactly accommodate the string so that it exactly replaces the region indexed. This
method uses the := operator for the assignment instead of = or == and it is the only
indexed string assignment that can affect the overall length of the string variable content.
See Chapter 5, Section 3 for further information.
4-22 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
4
The first function, TRIM$, removes any leading or trailing spaces from string L$ and
returns the intervening characters. The second function, REV$, returns the characters in
L$ in the opposite (reverse) order. The third function, STR$, converts the numeric value
of V into a printable string representation of that value. Functions are always of the same
form:
<function name> (<argument list>)
Input information to the function is specified after its name, enclosed in parentheses, as
a list of numeric or string values called an argument list. Each input value passed to the
function is called an argument and is specified using any general expression. The values
computed by these expressions are used by the function in forming its ultimate result.
The number of arguments and their type (string or numeric) depends on the particular
function being used. When more than one argument is present, they are separated from
each other with commas. A Data Type Error will occur if you specify a number (or string)
argument to a function that requires a string (or number) argument in that argument
position.
MegaBasic possesses a library of over eighty built-in functions and also allows you to
create your own functions, written in MegaBasic statements. Chapter 9 provides
complete descriptions of all the built-in functions in MegaBasic and how to use them.
Defining your own functions is a somewhat more advanced topic that is thoroughly
covered in Chapter 8. Refer to these sections for more complete details. The built-in
string functions are briefly summarized below:
The asterisks (*) indicate functions which return numbers (related to strings), rather than
strings. Although such functions are associated with strings, a function which returns a
string will be referred to as a string function in this manual unless otherwise noted. All
the functions above are described in Chapter 9, Section 3.
There is no significant difference between user-defined string functions and user-defined
numeric functions. String function names are formed exactly like string variable names
(similarly with numeric functions). A string result is returned from a string function, a
number is returned from a numeric function. Both may be defined with string or
numeric parameters and formats include both single and multiple line. See Chapter 8,
Section 3 for all the details on defining and using user-defined functions
4-24 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
4
Because unDIMensioned string variables are created by default when encountered for
the first time, misspelled string function names will result in variables being created
under those names. Such errors can be very difficult to diagnose because there is no way
for MegaBasic to detect the error. For example, STR$(I) returns the string
representation of the value of I, and ST$(I) returns the open-ended substring of
variable ST$ starting at position I.
MegaBasic provides three facilities to aid the discovery of misspelled names. First, the
NAMES command (Chapter 2, Section 3) displays an alphabetical list of all user-assigned
names in the program. You should check out unrecognized names that appear in this
display. Mistyped variable and function names tend to be displayed in close proximity to
the correct spelling of the user-assigned name, due to the alphabetical ordering. Second,
the XREF command (Chapter 2, Section 5) displays all references to any name. Names
with only one reference may be misspellings. Third, in program listings, MegaBasic shows
user-defined names in upper case and MegaBasic reserved words in lower case. If you see
one of your identifiers in lower case, then you better change it because it is a reserved
word.
This section provides descriptions of all statements involved in defining data structures
and moving computational results between variables. See Chapter 2 for the description
of the notation used to specify command and statement formats also employed in this
section. See Chapter 9 for all information about the built-in MegaBasic functions. This
chapter discusses the MegaBasic statements divided into the following categories:
GFK-0256 5-1
5
Data definition is the foundation of most computer programs and so we begin this
section by describing the MegaBasic statements for specifying data, defining data types
and allocating memory to data structures. These statements are summarized as follows:
DEF <vbl type specifiers> Declares the data types and uses for variables,
user-defined procedures, functions and structure
fields.
DIM <vbl size definitions> Creates string variables and arrays or changes their
current size or number of dimensions.
RESTORE <vbl list> Restores variables to their original contents at
creation.
DATA <data list> Defines string and numeric data for fast access
during program execution.
DATA END Terminates a logical group of DATA statements.
READ <vbl list> Loads data from a DATA lists into a set of program
variables.
RESTORE <pgm location> Randomly accesses a DATA list for subsequent access
by a READ statement.
ON...RESTORE <line list> Selects one of many DATA lists on the basis of a
computed index value.
LEN (<string vbl>)=<len> Sets the length of a string variable or string array
element without moving any data or changing
memory allocation.
REM <descriptive text> Descriptivecomments to assist your program
development and maintenance activities.
5-2 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
5
Data types are are assigned to names on a name by name basis, or by DEFining default
type assumptions based on the first letter of its name. Names that end in dollar sign ($),
percent (%) or exclamation point (!) are permanently assigned a string, integer or real
data type, respectively, and an error results if you attempt to declare such names
otherwise. The complete syntax for a type declaration DEF statement is as follows:
The <type> must be one of the words: STRING, INTEGER or REAL. The <list of names>
is a list of one or more names, separated with commas, to be assigned the <type>
specified. Names that will be used as arrays must be followed by empty parentheses ( )
to indicate that intention, for example X( ), Y( ) or Z( ). There is no comma between the
<type> and its subsequent list. See Chapter 3 for complete information about INTEGER
and REAL data types, and Chapter 4 for the STRlNG data type.
The <leading letter list> is a quoted string constant that assigns the specified <type> to
all names that begin with one of the letters listed. It affects only those names that have
not been specifically assigned a specific type in other DEF statements. Without any
<leading letter list> specifier, all letters default to REAL to conform with standard BASIC
type conventions and any undeclared leading letters will retain this REAL default. The
quoted string consists of any combination of individual letters, in any order. A dash (-)
between two letters specifies a range of letters (e.g., “a-z” means all letters). Upper and
lower case letters are interchangeable; commas and spaces can be inserted anywhere for
improving appearance and readability but are otherwise ignored. Letter declarations
also affect the types of user defined functions with names beginning with those letters.
Consider the following example:
This DEF statement assigns an integer type to variables X and Z, and to array Y. Then the
string constant ”i-n” defines variables with names beginning with any of the letters 1, J,
K: L, M or N as integer variables. Then define INCR, LVAL and N as real variables and
M1( ) as a real array. BUFFER is then declared as a string, along with any names that
begin with the letters 5,l u or v.
You can simplify a complicated DEF statement by breaking it into several DEF
statements, each defining a portion of the whole. The following set of DEF statements
are equivalent to the prior example:
DEF INTEGER X, Y( ), Z
DEF INTEGER ”i-n”
DEF REAL INCR, M1( ), LVAL( ), N
DEF STRING BUFFER, ”s-v”
Variables which you do not explicitly declare in a DEF will automatically assume a type
derived from the leading or trailing character of its name. Our example above explicitly
declares several variables as real, because the leading letters in their names were
declared as integer by the string constant ”i-n”.
DEF statement must appear as the first statement of the program line on which they
reside and begin with the reserved word DEF. A program can have any number of DEF
statements, which may appear anywhere in a program. When encountered during
execution, DEF statements are skipped as if they were REMark statements.
If you declare the same name or leading letter as having more than one data type, a
Double Definition Error will occur. If you declare a name as one type and its leading letter
as another type, the specific name declaration will win and no error will be reported.
Declaring specific names always overrides letter declarations that would have affected
those names.
5-4 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
5
Arrays may be DIMensioned as string, integer or real (floating point). The words
STRING, INTEGER and REAL can be placed in the VIM specification list to control the
data type of subsequent variable definitions in the list, for example:
DIM INTEGER A(100),B(30),REAL C(75),D(150),
STRING LINE(80),Q(20,30)
In this example, arrays A( ) and B( ) will be integer arrays, C( ) and D( ) will be floating
point arrays, LINE( ) and Q( ) will be strings. Notice that the type specifiers STRING,
INTEGER and REAL affect all array definitions that follow them, until a different data
type is specified by a subsequent type specifier. Data Type Errors will occur if you
attempt to DIMension names that end with $, ! or % after a conflicting type specifier
[e.g., STRING V%(50), INTEGER X! (100) or REAL A $(80)].
When arrays are DIMensioned with their data type explicitly specified as described here,
any data type assigned by previous DIM statements or DEF statements is overridden
because the latest DIM statement always takes precedence. When no type specifier (i.e.,
STRING, INTEGER or REAL) is explicitly given in the DIM statement, the previously
assigned type will prevail. The following program illustrates how this works:
Def integer X( ); Rem - X is declared an integer array
Dim real X(100); Rem - Create real array X, override DEF decL
Dim X(750); Rem - Change its size, but keep Its REAL type
Dim integer X(5); Rem - Now DlMension X as an integer array
Dim X(8000); Rem - Change its size, but keep its integer type
Dim string X(50); Rem - Now X is a 50 character string vbl
Dim X(256); Rem - and then change X(J to a 256 char string
Changing the type of a variable is only supported in the interpreter. The MegaBasic
compiler does not support it, preventing such programs from being successfully
compiled. Changing the number of dimensions in an array or their size is supported by
both systems.
When arrays are created, each of their elements is given an initial value as part of the
array creation process. Each of the elements in numeric arrays is set to zero. Each string
array element is set to contain a string of spaces of the maximum string element length
specified. Simple string variables are also initialized with a maximum length string of
spaces. PARAM(7) may be used to change the fill byte of initialized strings to any other
ASCII character if spaces are not desired.
See Chapter 3, Section 4 and Chapter 4, Section 2 for additional information about
DlMensioning arrays and strings. Section 3 of this chapter contains a great deal of
pertinent information regarding numeric types and how you go about choosing and
specifying integer and real variables.
scalar string variable B$ and all elements of string array R$( ) with spaces. Such use of
RESTORE is unrelated to its use with DATA statements.
DATA END
Specifies a logical end-of-data. Normally when a READ statement encounters the end of
a DATA list, it skips ahead to the next DATA statement and continues on. However in
many programs, you may have several groups of DATA statements that are not related to
5-6 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
5
one another. In such a case, falling through one group into the next is totally meaningless,
but an error may not be reported, causing improper program operation that can
potentially be difficult to find.
Therefore, to protect yourself from such problems you can place a DATA END statement
after each logical group of DATA statements. If encountered while READing DATA
statements, MegaBasic reports a Missing Data Error instead of READing ahead to the next
DATA statement. Once encountered, your program has to reset the DATA pointer using
a RESTORE statement before any further DATA can be READ. DATA END is not
mandatory but MegaBasic provides it to assist the program development process.
RESTORE [<label>]
Sets the DATA READ pointer to the first DATA statement after a specified line number or
line-label or to the first DATA statement in the program by omitting the <label>. This
statement is used to reset the READ pointer or to provide random access to DATA
statements. After being RESTORed either to the first or some other DATA statement,
subsequent READ statements will access DATA statements sequentially from that point.
Such use of RESTORE is unrelated to restoring variables as described earlier in this
section.
list>. The <line list> consists of a sequence of line numbers and/or line-labels, separated
with commas, which specify program locations of DATA statements. The DATA READ
pointer is set to the first DATA statement on or after the line selected via the integer. This
is useful for selecting data through a multi-way computational decision, an extended
form of the RESTORE statement above.
5-8 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
5
You can transform numerical and string information by combining one or more data
items into a result using arithmetic, mathematical, logical, or other computational means.
The result can then be assigned to a program variable or used in further calculations. You
need to understand how to use numbers, constants and variables (Chapter 3), strings,
string variables and string expressions (Chapter 4), and string and numeric functions
(Chapter 9), to make full use of the data transformation statements summarized below:
Numeric variables and expressions are of two types: integer and real. Usually real values
are assigned to real variables and integer values are assigned to integer variables.
However MegaBasic does permit mixed-mode assignments of either type (i.e.,
integer=real, real=integer). Such assignments are inherently slower because of the
necessary conversion of the assigned value into the numeric type of the receiving,
performed automatically by MegaBasic.
An integer value can be converted to a real value without precision loss in all cases
except one. Integer values beyond 100 million (+–) cannot fit within 8-digit BCD
floating point representation. Therefore the value is truncated to contain only the
leading 8 decimal digits of the integer. Values between 100 million and 1 billion will
always be within 9 of the actual value; values over 1 billion will be within 99 of the
original integer value after being converted to real. We strongly recommend that you
use versions of MegaBasic with 10 or more digits of floating point precision to avoid this
conversion limitation. If your program never uses integers of this size, 8-digit MegaBasic
can be used without any difficulties.
5-10 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
5
When you specify an extended assignment operator (i.e., +=, –=,*=, /=, mod=, div=,
^=, sgn=), be sure that there are no spaces between the leading operator and the
following equals sign (=). For example X + = 1 will be reported as an error, while X +=
1 will be accepted.
When MegaBasic evaluates extended assignment statements, the expression to the right
of the equals sign (=) is completely evaluated before being combined with the variable
to the left of the operator, therefore:
the assignment: X *= Y + 1
is evaluated as: X = X* (Y+1)
instead of: X = (X * Y) + 1
Extended assignments are especially useful when the target variable is an array with
complicated subscripting, because of the time saved by omitting the second reference to
the variable. You can use extended assignment statements only with numeric varia~les;
extended string variable assignments are not supported. Furthermore, the numeric
operators supported include only those shown above and do not include any logical
operators (e.g., AND, OR, XOR, etc.) or comparison operators (e.g., = <~ > < etc.).
Proper use of assignment expressions can improve the performance of your software by
reducing the number of statements and variable accesses required to process a given set
of operations. Any kind of assignment can be used in assignment expressions, including
numeric and string variable assignments, extended assignments to numeric variables
(e.g., += –= *= etc.), and string replacement (:=) assignments. When string
assignment expressions are specified, the string returned by the assignment is the string
that was actually stored into the variable (which may be truncated to fit). The following
example evaluates a string expression, stores it into A$, compares the value stored into
A$ with B$ and increments N if they compared equal:
N += (let A$ = STRING_EXPRN$) = B$
Avoid assignment expressions involving the same variable more than once within the
same expression, because the order in which terms are evaluated within a complicated
expression is not necessarily well-defined. For example, the MegaBasic compiler
evaluates the individual terms of certain expressions in a different but equivalent order
for performance reasons.
You should also be aware that since the right-hand operand of AND, OR and IMP
operators is sometimes left unevaluated, LET assignments in that term may not be
performed. For example in the expression X OR (LET Y-Z), Y is set to Z only if X is
nonzero. This is because the expression result is known to be 1 if X is nonzero, causing
MegaBasic to skip the right-hand term (along with the assignment) to improve
performance.
The statement computes the value of X*Y/Z and then stores it into E, followed by D,
then C, B and A. The leading LET is actually unnecessary, but was added merely for
consistency and style. Be careful when using compound assignments so that the value is
preserved as it passes from variable to variable. If integer and real variables are being
assigned, there may be some truncation due to the implicit type conversions that occur
when real values are stored into integer variables. Furthermore in compound string
assignments, the string value will shrink as it passes from right to left if any of the
assigned variables are not as large as the string they are receiving.
A useful routine that employs assignment expressions is given below. This routine
displays the contents of BUF$ onto the screen and expands any tab characters (ASCII 9
codes) into the appropriate number of spaces.
Notice that the WHILE condition expression sets I,J to point to the next sequence of
characters that does not contain a TAB character. This simplifies the loop down to only
one statement (the PRINT) and improves the processing speed accordingly.
5-12 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
5
In these examples, characters above the specified indexed region in A$ are moved
appropriately and the length of A$ is adjusted up or down accordingly. If you specify a
destination anywhere beyond the end of the destination string, the string on the right is
simply appended to the end of the destination string on the left, for example:
If the operation implies a total result which is longer than the DIMensioned limit to the
destination string variable, all result characters beyond that limit are lost, as occurs with
the standard string assignment statement. Although this assignment is designed to give
the exact same result as if it were programmed using the standard concatenated
assignment, it does it 2-10 times faster and in a much more obvious way as shown by the
equivalent methods given in the examples.
This is a true string replacement operation that has many important applications. The
following example illustrates how easy it is to substitute one string for another, of
differing lengths, throughout a large body of text:
Line 30 sets I to the location of the next occurrence of C$ in T$ using the FIND function
(Chapter 9, Section 3). Line 40 performs the substitution and then repeats the process,
which ends when no more occurrences are found. This is a near optimum replacement
procedure and illustrates just one typical application of the string replacement
statement.
5-14 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
5
Evaluates the non-negative numeric expression (on the right), converts it to an integer,
and assigns the result to any bit subrange (up to 24 bits wide) within a string variable.
The string variable reference may be indexed or unindexed. The optional <bit range>
specifies a starting bit position and either an ending position or a number of bits to be
affected. The following examples illustrate the various possibilities for specifying bit
ranges:
The BIT function is capable of accessing groups of 1 to 24 bits as a numeric unit. This
provides very efficient utilization of memory when large tables of small positive integers
are required. A string with a byte length of L bytes has bit positions from 0 to L~1,
which can be as high as 524015 (for the largest possible string of 65502 bytes).
If you specify a bit range that lies partially beyond the last byte of the string, the bit
range is truncated to fit the actual string. A bit range consisting of zero bits is specified if
all bits in the range lie beyond the string. BIT requires that the actual bit string being
accessed consists of 1 to 24 bits in length and lengths outside this range result in an Out
Of Bounds error.
The first bit of the specified bit subrange always represents the high order bit of the
integer bit sequence being accessed. The table below illustrates the relationships
between BIT addresses, bit numbers within bytes, and character
For example bit4 of the 3rd byte of the string is in BIT position 19. The BIT positions in
the table go by fours only to simplify illustrating the idea. BIT( ) may appear on either
side of the equals sign depending on whether you are storing a value (left) or accessing a
value (right). Other string functions that deal with bit strings include ROTAT$, ORD and
CARD (Chapter 9, Section 3). Chapter 4, Section 4 describes various boolean (logical)
operators which may be used combine and manipulate entire bit strings in one
expression.
Values are stored modulo 2^width, causing reduction of values too big for the given
length. For example BIT(A$,95:8)–259 stores a value of 3 into the 8 bits starting at bit 95
of A$. This is because the value 259 is actually 9 bits wide and only the lowest 8 bits were
stored.
5-16 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
5
The expression to the right of the equals sign must return either an integer or a real
numeric result; a Data Type Error occurs if a string expression is specified. To obtain the
best performance, you should specify integer numeric expressions (rather than real)
wherever numbers are required on either side of the equals sign. Real values are
internally converted to integer representation before they can be used for string
indexing, array subscripts, bit positions and bit widths, and this conversion is inherently
time-consuming. Integer values are already in the proper internal form for immediate
application.
In addition to its ability at packing and unpacking small integer values in and out of
string variables, the BIT statement has important applications in processing bit strings.
Bit strings are ideal for representing sets, where bit(i) of the bit string is set to one if set
element (i) is a member of the set and reset to zero if it is not a member. The BIT
statement can, of course, set or reset any bit in a bit string. Additional information about
bit strings can be found in Chapter 4, Section 4 and Chapter 5, Section 2.
BIT has additional applications in sorting. Multi-byte values written into bit fields are
ordered high byte to low byte, permitting such fields to string sort correctly. This is the
opposite order from the way multi-byte integers are transferred using FILL, EXAM,
READ and WRITE statements. BIT is particularly useful for packing values into a string
variable used to communicate the CPU register contents in CALL statements (Chapter 7,
Section 3).
Many applications involve complex record structures where a single data item may
contain numerous related strings, reals and integers. To be able to move these collections
of values or field structures around as a unit can greatly simplify programming and make
processing more efficient. MegaBasic supports field structures as data templates into string
variables, enabling you to assign names and data types substring regions within string
variables which can then be accessed by name. The resulting super-strings can then be
processed using the rich string facilities of MegaBasic just like other strings.
5-18 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
5
(3) @<position>
Defines the string index to assign to the next structure field name in the list. The at-sign
(@) must appear in front of the <position> value so that MegaBasic can tell it apart from
forms (1) and (2). Since they are equivalent to string index positions, it is an error to
specify a negative or zero <position>. This form is used when the sequential assignment
of field positions is not desired or to override other defaults imposed by the STRUCT
statement.
The <position> value is relative to the subfield list it is specified within, or relative to 1 if
not in such a list, for example:
STRUCT FIRST$:40, SECOND$[A$:20, @11, B$:30]
This defines B$ within a position relative to SECOND$, equivalent to SECOND$(11), or
an absolute position of 51. It also implies that SECOND$ has a length of 40, because A$
and B$ overlap by 10 positions.
STRUCT( ) Function
A special function useful in <position> expressions is STRUCT(F), which returns the
string index assigned to field F. STRUCT(F,N) returns other information about field F
depending upon the value of N, as follows:
5-20 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
5
Fields completely defined in DEF STRUCT statements do not generate any code in
compiled programs, reducing your executable program size accordingly. Fields defined
in DEF STRUCT statements can always be redefined by subsequent STRUCT statements
during program execution, but remember that the leading field of a STRUCT statement
takes on its prior position if already defined. DEF SHARED STRUCT fields do not require
any actions in the package prologue to create them.
When developing and debugging your software and you modify a CONTinuable
program, MegaBasic re-processes all DEF statements to reflect any changes you may
have made in DEF statements, as well as to catch conflicts. However, re-processing a DEF
STRUCT statement would cause all statically defined fields to revert to their original data
types, positions and field widths. Any changes to those fields made by subsequent
STRUCT statements during prior program execution would be lost by such a move,
upsetting subsequent program CONTinuation. Therefore, DEF STRUCT statements are
ignored on DEF statement reassertion passes whenever you modify a CONTinuable
program. Although this prevents corrected DEF STRUCT statements from taking effect
while debugging a CONTinuable program, it does preserve the current program
execution state that is vital to CONTinuing after making program changes.
With structure fields, you can access integers, floating point numbers and fixed-length
strings within some larger string variable by name instead of using indexing expressions.
Using the extremely efficient string operations of MegaBasic, collections of such variables
can be moved, read, written or compared many, many times faster and with far greater
simplicity than processing the same set of variables individually. You refer to structured
variables in programs in the following manner:
Variable.field
where the field is a name to which you have assigned a string position, a length (number
of bytes) and a type (i.e., integer, real or string). The Variable portion must be a reference
to a string variable, which is then accessed through the field name specified. The string
variable may be indexed, or specified as a structured field of a larger string variable,
which allows for an indefinitely long path of fields. For example:
RECORD$.PERSON$.ADDR$.ZIP
This might refer to an integer ZIP code that resides in the ADDR$ field of a PERSON$,
which in turn, is a field in a larger RECORD$ that could contain many other fields. The
rules for specifying a structured variable reference are as follows:
5-22 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
5
h Any string pathname field may be indexed, so that field names that follow can
refer to the indexed region of the string instead of the entire string field.
h Numeric fields (real or integer) may be followed by an optional pseudo array
subscript, to refer to the nth number instead of the first number at the specified
location. For example, A$.COUNT and A$.COUNT(0) both refer to the same
COUNT field, but A$.COUNT(1) refers to the number in the bytes that
immediately follow A$.COUNT(0). Only 1-dimensional subscripts are
supported; an error is reported if you specify two or more subscripts.
h Numeric field references are supported in any vector context. The effective
vector length is determined from the number of elements that fit into the string
variable region accessed by the field.
Remembering that each STRUCT list begins with a default position of 1, the above sequence
defines the following structured variable fields:
Using these definitions, some of the meaningful pathnames that are possible are listed
below:
5-24 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
5
Notice that the structured variable references can be applied to ANY string variable (that
is long enough to contain the field being accessed). Also notice that, since the sub-field
lists are all based at position 1, you have to specify full pathnames to access any field
correctly. However, you can also define this structure so that any field can be referred to
using only a single field name in the path. This is done as follows:
The access to fields using this layout is a little faster than the prior method because fewer
names are used to determine the position of each field accessed. The later method is also
less wordy and brief as compared with the former. However, the prior method may be
more flexible in some applications because the sublevels defined can potentially be used
within different data structure hierarchies.
Default Referencing
References to structured variables normally begin with the name of an actual string
variable, which is then followed by a path of field names. In real life applications, this
name might be the same for a large proportion of all such references. Therefore
MegaBasic provides a method for declaring a string variable to be accessed by default
whenever you omit the leading variable name from the structured variable reference
pathname. This can greatly simplify and shorten certain complicated expressions
involving such references and reduce the running time needed to evaluate them.
Defining the default host variable involves the following statement:
where the <string variable> may be a scalar string variable name, and indexed string or a
string field reference. String array references are not supported. This statement declares
the string variable to be used, by default, whenever you omit the variable name from a
structured variable reference. You can also specify the word CLEAR instead of <string
variable> to cancel the current default. An error is reported if you specify neither a scalar
string variable nor the word CLEAR.
The default remains in effect for all subsequent statements within the current package
until a different default name is declared. You cannot affect the current default structure
of other packages: each package has its own default. Also, you can localize the default
within procedures and functions by placing the word STRUCT in a LOCAL statement
(e.g., LOCAL X,Y,STRUCT,Z$) . This lets you change the default name within a
procedure or function without upsetting a possible default already declared around the
call to that subroutine.
The current default host variable has two distinct effects upon structured variable
references depending on whether or not a default was in effect when the variables were
defined in a STRUCT statement, as follows:
h Fields defined while a default variable is in effect are assigned a permanent
default that persists even after another STRUCT USE statement selects a different
default.
h Fields defined with no default variable in effect are assigned temporary default
status. References to such structured variables always use the currently selected
default as a temporary default; references made with no default in effect are
reported as a Structured Variable Error. When a subsequent STRUCT USE
statement selects another default variable, these same references follow suit and
access the new variable instead.
For example, consider the following sequence:
10 STRUCT A,B,C; Rem -- Defined with temporary default status
20 STRUCT USE A$; Rem -- Select A$ as the current default
30 STRUCT X,Y,Z; Rem -- Defined with permanent default of A$
40 STRUCT USE B$; Rem -- Change current default to B$
Line 10 declares several structured fields. Line 20 declares the default name to be A$, so
that now A, B and C all refer to A$ by default. Line 30 then defines three more structure
fields, X, Y, and Z, and assign each the permanent default of A$. The in line 40 we change
the current default name to B$. At this point, A, B and C now refer to B$ by default, but
X, Y and Z still refer to A$ because they were defined with a permanent A$ context.
The default variable idea can be used in very powerful ways. For example a subroutine
might refer to a set of variables which are, in fact, structured fields using the current
default variable. By modifying the default variable then calling this subroutine, you can
control the set of variables that it uses. This default only affects variable accesses within
the package it was defined in. Each package can independently define its own
structured variable default without affecting the others. Remember, however, that
default variables only come into play when you omit the leading host variable name
from the front of a structured variable pathname.
5-26 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
5
Pointers are supported under MegaBasic in a manner very similar to the pointer facilities
provided by C, PASCAL and other programming languages. A pointer is a mechanism
for accessing variables without using or knowing the names of those variables. In place
of a name, an identifying number or address is used, called a pointer. The real power of
this is that unlike names, pointers can be stored in other variables, moved around and
manipulated arithmetically. As a result, the choice and access of variables is controlled by
the executing program instead of being fixed within the program source code.
MegaBasic pointers can refer to user-defined functions, procedures and line labels, as
well as to variables. Furthermore, a pointer can refer to an entire array or to one element
within an array. To obtain the pointer associated with a named variable or other entity,
simply precede its name with a caret (A), as follows:
PTR = ^OBJECT
After executing this assignment statement, the variable PTR contains a pointer to the
variable OBJECT. If OBJECT had never been defined or assigned a previous value, a
pointer value of zero would be assigned to PTR (an invalid pointer that refers to
nothing). To access OBJECT using this pointer, you have to precede the pointer value
with an asterisk (*), as follows:
*PTR
This symbol can be used to specify OBJECT in any context where you could specify the
name OBJECT. For example, the following representations show how pointer
references correspond to name references:
Arrayvariable
PTR = ^ARRAY *PTR(i,j) ==
ARRAY(i,j)
Array element
PTR = ^ARRAY(i,j) *PTR ==
ARRAY(i,j)
Function Call
PTR = ^FN_ADD *PTR(x,y) ==
FN_ADD(x,y)
The caret (^) function extracts the pointer to a named object and always returns an
integer value. Its argument can be any variable, label function, procedure or array name,
or it can be an array element reference. Both string and numeric variables are supported.
If you specify an array name without subscripts, then the resulting pointer must be
followed by subscripts in all asterisk (*) references. A pointer derived from a subscripted
array name must be used without subscripts, i.e., the *pointer reference behaves just like
a scalar variable. Except for subscripted array references, the caret function (^) accepts
no constants or other expressions of any kind. When extracting a pointer to a function or
procedure, do not specify any of its arguments in the pointer (^) function: only specify
its name by itself.
5-28 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
5
Pointers are only valid within the scope of an executing program. As such, they can not
be written to a file and then read back in a later invocation of the program. This is
because pointers are related to the physical memory location of the objects they point to,
which can change from one invocation to the next. A pointer to an object remains valid
throughout the life of the object it points to. For example, a pointer to a function in
another package is valid until that package is no longer in memory. You are responsible
for ensuring that pointers are valid before you use them. If MegaBasic determines that a
pointer is not valid, it reports a Pointer Variable Error (type 41).
Although a caret (^) is also used in MegaBasic as a power operator and an asterisk (*) is
also used as a multiply operator, there is no program context from which the meaning of
either of these symbols cannot be determined. In other words, there is never any
confusion or ambiguity. This property is similar to that of the minus sign (–), which is
used for both subtraction and negation.
5-30 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
5
Pseudo Variables
Using the pointer facility of MegaBasic, you can manipulate variables without knowing
of or dealing with their variable names. Normally, such variables had to be named
somewhere, i.e., they had to exist as ordinary variables before you could access them
through pointers. A special function lets you to create new variables whose only access is
through pointers, as described below:
P = CREATE(Q)
where Q is a pointer to a variable of the type desired for the new variable and P receives
the pointer to the new variable returned by CREATE(Q). There is no relationship
between variables *P and *Q except that they have the same type and *Q is not affected
in any way. The above assignment statement reads: Create a new variable with the same type
as variable *Q and store a pointer to it in P. Variables created in this manner are called pseudo
variables, because they do not have the usual name associated with them.
Arrays and strings created by this function have to be DIMensioned before they are used,
as no memory is allocated to them at creation time. If you access such variables without
DIMensioning them, the usual default variable DIMensions will be created automatically
as part of the first access. You can create scalar or array variables in any of the three
types: integer, real or string. An error results from attempting to create structure fields,
procedures, labels or functions.
Variables created by CREATE( ) are owned by the MegaBasic package that executed the
CREATE( ) function. If this package is removed from the system during execution, all
pseudo variables it owns will also be removed and their allocated memory released back
to the system for subsequent general use. Subsequent attempts to access variables that
no longer exist must be avoided, because the results will be extremely unpredictable and
can crash the system.
In some applications, you may want to create and then later release pseudo variables at
some point in your program without having to DISMISS the package that created them.
To free any variable from the system, use the following statement:
MegaBasic has a maximum capacity for 8190 symbols over all packages of a running
program (which includes functions, procedures, labels and fields, as well as variables).
Therefore, applications that expect to create vast numbers of pseudo variables may not
succeed. Small programs can create more pseudo variables than large programs because
the application begins with fewer symbols to start with. If you exceed the symbol
capacity, your program terminates with a Too Many Symbols Error. Typically, however,
even large programs with many packages use only several thousand symbols, which still
leaves most of the symbol capacity available for pseudo variables. Use the FREE(3)
function to find out how much symbol space remains.
5-32 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
Chapter 6 Program Control Statements
6 section level 1 1
figure bi level 1
table_big level 1
Conditional Execution Statements that allow the results of calculation and com-
parison to decide subsequent program behavior.
Loops and Iteration Statements to setup and control blocks of statements
Control repetitively.
Some of the program control facilities involve specifying where to go for the next
statement to execute. Line numbers are the easiest means to indicate program locations
for such purposes. However MegaBasic lets you to assign names to lines, called
line-labels, that may be placed at the beginning of a line, separated with a colon, as in the
following example program line:
10 LABEL: C = C+1; If C<100 then Goto LABEL
Line-labels may be any name legal as a variable name and may end with a dollar sign if
so desired. MegaBasic names follow certain rules which are laid out on Chapter 1,
Section 5. Once a name is used for any purpose in a MegaBasic program you cannot use
the same name for any other purpose (i.e., they must be unique). Once a line has been
given a line-label it may be referred to either by line number or by line-label. In other
words, line-labels and line numbers are interchangeable when referring to a line. If a
line-label is not followed by any statements on the same line (i.e., the line consists solely
of a label and a colon), references to that label will actually refer to the next line.
Line-labels on lines by themselves can be useful for making the label stand out to
improve readability.
The pseudo-line-label NEXT may also appear anywhere that line numbers and line-
labels are expected. This special reserved word, when used like a line number, refers to
the location in the program of the nearest closing NEXT statement. This feature is
discussed more fully in the context of the NEXT statement, described later in this section.
It is of course an error to refer to a line number or a line-label which is not present in the
program as specified. MegaBasic does not find references to things that do not exist in
your program until they are encountered during actual program execution. However,
GFK-0256 6-1
6
the CHECK command (Chapter 2, Section 4), will find many of these dangling references
along with others kinds of errors that may also be present.
6-2 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
6
The simplest and most direct of all program control statements are described here, which
merely cause execution to either begin somewhere else in the program or terminate
execution in a variety of ways as summarized below:
GOTO <label>
Causes program execution to continue at the line number or line-label specified. This is
sometimes referred to as an unconditional branch. A Line Number Error results if the line
does not exist as specified. The line label referred to must be in the same program as the
GOTO statement. When multiple programs or packages are in memory, the only way to
transfer control between them is by PROCedure or FUNCtion calls: GOTOS and other
line references are not allowed.
The GOTO keyword is optional when it is the object of a THEN or ELSE clause in an IF
statement (discussed later in this section). Do not use a GOTO to permanently exit a
subroutine of any kind (function, GOSUB or procedure). This is because MegaBasic
supports recursive programming and therefore assumes a subroutine is active until a
RETURN statement (Chapter 8, Section 1) is executed. However, you can jump out of a
GOSUB with a GOTO as long as a RETURN statement is eventually encountered, such as
jumping to another GOSUB.
If a GOTO is used to branch out of a FOR, WHILE or REPEAT loop, the loop will be
terminated and execution will continue normally. Any number of nested loops can be
terminated by one such GOTO and MegaBasic always continues properly at the nesting
level in effect in the line specified. GOTOS of any type all operate in this manner. See the
FOR, WHILE, REPEAT and NEXT statements for more details. If the line label specified is
the keyword NEXT, MegaBasic branches to the beginning of the next current-level FOR,
WHILE or REPEAT loop iteration.
A GOTO inside a multi-statement THEN or ELSE clause terminates the IF statement
operation, i.e., it is assumed to always jump out of the clause. If its target is inside the
clause, an error will occur as soon as the closing bracket a) is encountered.
6-4 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
6
Together with the looping mechanisms of MegaBasic (Chapter 6, Section 3), these
statements provide everything you need to control program execution without resorting
to GOTO statements (which tend to obscure program structure and should be avoided
whenever possible).
You can omit the ELSE clause from any IF statement (i.e., the word ELSE along with
<statement2>). If you do, then the IF statement simply controls whether or not
<statement1> is executed. The examples below show some simple IF statements and
how they work:
Although the <logical exprn> is usually a simple comparison of some type (e.g., IF X=Y
THEN...), an expression of any complexity is permitted as long as it produces a numeric
result and the entire <logical exprn> fits on the same line. Any combination of string
comparisons, numeric comparisons and general numeric expressions are permitted, for
example:
6-6 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
6
The various kinds of things your can specify in THEN and ELSE clauses are summarized
below:
Any single Any single statement that can be executed by itself can be placed
statement after a THEN or ELSE, including another IF statement
Compound Two or more statements separated by semicolons and surrounded by
statement square brackets [...]. These are discussed in detail on the next page.
Line number The GOTO reserved word is optional on GOTO statements that immedi-
or label ately follow THEN or ELSE. This is called an implied GOTO statement.
FOR, WHILE Any well-defined multi-statement loop can form a complete THEN or
ELSE clause without requiring brackets [ ] around the loop, i.e., the
or REPEAT loop is treated as a single statement.
Compound Statements
For greater expressive power, either <statement1> or <statement2> or both may be a
compound statement, which is several ordinary statements grouped together and
executed as a unit. To form a compound statement from several individual statements,
surround them with brackets [ ]. Compound statements only appear within IF
statements (after a THEN or ELSE) and may extend indefinitely over one or more
program lines. The following example should clarify their use:
Notice that FOR..NEXT (and WHILE..NEXT) loops may be included within compound
statements. When an IF statement is employed within a compound statement, it too can
include compound statements for its THEN or ELSE clauses. You can use the bracketing
mechanism to override the normal precedence of ELSE clause processing whenever
required, for example:
In this example, the ELSE refers to (by default) the second IF which is only executed if
X-Y. Suppose that the desired action is to execute the ELSE clause upon failure of the first
IF test (X=Y). This can clearly be done as follows:
Multi-line IF Statements
Although, for the most part, IF statements tend to be short enough to fit completely
within the same program line, IF statements may span any number of program lines.
This lets you construct multiple level IF statements of any complexity the way you can
in PASCAL or C. A multi-line IF statement in MegaBasic works just like the simpler
single-line IF statement, except that the THEN and ELSE clauses can extend beyond the
one-line limit and physical line breaks can appear almost anywhere within the ~
statement. Specifically, the following rules and limitations must be observed:
h The IF and <logical exprn> components must fit in and appear on the same
program line (which can be up to 255 characters long). Condition expressions
longer than this are unusual, but they can often be broken up into smaller pieces
that fit the form:
If <cond1> then If <cond2> then If <cond3>...
h Physical line breaks may occur anywhere except within a simple statement or
between the IF and the <condition> expression. For example, a line break may
occur on either side of the THEN or ELSE keywords or on either side of a
compound statement bracket.
h Any branch to an explicit line number or label from within an statement will
logically exit the IF statement. Hence you cannot use GOTOS within IF
statements except to completely jump out of them. However, loop-relative
branches can be used without exiting the IF (i.e., EXIT, GOTO NEXT, etc.) when
the loop resides entirely within a THEN or ELSE compound statement.
h GOTOS that branch into the middle of an IF statement will generally lead to an
error condition. This is because an IF must be entered from the top in order to
properly process THEN and ELSE clauses and deal with compound statement
brackets properly. MegaBasic does not detect such errors until it encounters an
unexpected THEN, ELSE, or bracket [ ].
h Only use a GOTO within a clause to jump out, never to jump within the clause.
MegaBasic treats all such jumps as IF clause exits and an error will occur if they
don’t actually exit.
h Functions, procedures and GOSUBS can be invoked from within IF statements
without any effect upon the state of the IF, even if GOTOS or other (unrelated)
IF statements are executed within them. Recursive re-entry into an active IF
statement is also supported.
6-8 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
6
When a CASE block is encountered during program execution, the following things
happen:
h MegaBasic verifies that the CASE BEGIN statement is matched by a later CASE
END statement (an error results if none is found).
h Second, the CASE argument is evaluated and saved for later use in the CASE
tests. For certain reasons described below, this argument is optional.
h MegaBasic scans each of the CASES within the CASE block to compare the CASE
argument with the CASE test expressions. If the CASE argument matches one of
the CASE tests, the sequence of statements associated with that CASE is then
executed, after which execution continues on the first statement after the closing
CASE END statement.
If none of the test values match the CASE argument, none of the CASES is executed, the
CASE block is exited and control passed to the first statement after the closing CASE END
statement. To help understand how all this works, consider the following program
fragment:
Most elements of the CASE statement are represented in this example. Line 210 defines
the beginning of the CASE block and evaluates the CASE argument. If X=1 then the
CASE test on line 220 is satisfied, and MegaBasic proceeds to execute all of the statements
following the CASE test up to the next CASE statement. This sequence of statements is
not limited to one line, and may potentially span many lines or even pages before being
terminated by a subsequent CASE test or the final CASE END statement. A CASE branch
may contain no statements at all. Such a null branch has the effect of exiting the CASE
block when its corresponding test succeeds.
Notice that the second CASE test (on line 230) contains two values. CASE tests consist of
one or more values, separated from one another with commas. Each test value is applied
in turn until one of them succeeds or all of them fail. Furthermore, the CASE test values
may be specified as expressions, instead of being limited to simply constant values (as in
CASE statements of other languages). This is illustrated on line 240 where the CASE
argument (X) is compared with the value Z+3.
You can also specify a CASE test without any values, which creates a test that always
succeeds, as shown on line 260. Such a CASE test is used to execute a group of statements
in the event that none of the other tests have been satisfied. Naturally, if you use this
capability, this empty test must be the last one in the block (before the CASE END) because
any others that follow could never be reached.
The on value of the CASE BEGIN statement is optional. By omitting it, all CASE test values
are treated as conditional expressions, like the one used in IF statements where, instead of
comparing the CASE test values with a CASE argument, MegaBasic evaluates each test
expression as a true or false (i.e., non-zero or zero) and the first one that evaluates to true
is the CASE branch taken. The example below shows how this is done:
This feature lets you create your own test conditions when the simple equality method
isn’t adequate for your application. For example, one CASE test might test X<Y, and the
next CASE might test V>LOW AND V<HIGH, and so on.
When omitting the CASE argument, specify CASE BEGIN by itself without any ON
<expr n> clause. An Out Of Context Error results if you specify a comparison operator in
front of any conditional CASE test expressions, as this does not have any meaning. A
complete summary of CASE statement usage and operation follows.
6-10 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
6
The CHECK command will report any CASE blocks that do not contain matching CASE
BEGIN and CASE END statements. The display produced by the TRACE RET command
will show an entry for each active CASE block. Both of these features are useful during
the development, testing and debugging of applications that use CASE statements,
especially nested CASE blocks.
numeric and, if necessary, will be converted to the same numeric type (integer or real) as
the CASE BEGIN argument before each comparison is made. To avoid unnecessary type
conversions, arrange for numeric arguments and test values to have the same numeric
type. Logical test expressions may evaluate either to integer or real without incurring
any conversion penalty.
6-12 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
6
Loops are sequences of statements that are executed over and over again and usually
represent the areas in your program where most of the time during execution is spent.
The following loop constructs are provided in MegaBasic to support different ways of
sequencing and terminating loops:
6-14 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
6
Loops must terminate eventually, or your program would never come to an end. FOR
loops will terminate when all the values specified for the index variable have been
exhausted. You can however terminate FOR loops in other ways, all of which are
summarized as follows:
h Falling out the bottom of the loop after the index variable has taken on the last
value of the last series. In other words, letting the loop run its normal course.
h Branching out with a GOTO or implied GOTO (a THEN or ELSE <label>). You can
jump from an inner loop to any outer loop with any number of levels in
between, as long as you do not jump beyond the current subroutine level.
h Branching out using the EXIT statement (Chapter 6, Section 3).
h Executing a RETURN statement (Chapter 8, Section 1), which exits not only the
loop but its matching GOSUB, user-defined function or procedure as well.
h Causing an error which is trapped by an ERRSET statement (Chapter 6,
Section 4) at some higher level.
For efficiency (as well as for recursive reentrance), MegaBasic maintains a FOR..NEXT
internal structure in the scratchpad area for the lifetime of the loop. If you terminate a
loop using any of the above methods, MegaBasic automatically recovers the loop
structure appropriate to the context into which you jumped. Two types of jumps are not
meaningful however
h Branching into the middle of a lower, inner loop is illegal because the inner loop
has not been initialized.
h Branching into a higher, outer loop active in a subroutine at a higher level is
illegal because MegaBasic cannot leave a subroutine without executing a
RETURN statement.
At times, you may wish to skip the remainder of the current loop iteration and begin
again with the next iteration. For example you might be looping through all the elements
of an array and performing some computation only if certain criteria are met. To do this,
you need only branch to the NEXT statement that terminates the loop. A GOTO can do
this if the NEXT is the first statement on its line. However, MegaBasic will automatically
begin the next iteration of any loop if you say GOTO NEXT (also ...THEN NEXT ...ELSE
NEXT or any other form of GOTO). This is further explained by the discussion on the
NEXT statement in Chapter 6, Section 3.
6-16 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
6
The first example does nothing if X is already 100 or greater. The second example
illustrates what happens when the logical expression is not altered in the body of the
loop. It may be that you desire such an infinite loop in your application because you
employ other means to terminate the loop, for example:
While Y=Y; X=X+1; IF X>=100THEN EXIT; NEXT
This example illustrates the use of the EXIT statement (described below) in terminating a
WHILE loop, bypassing normal termination. The methods for legally terminating WHILE
loops are summarized below:
h Causing the condition to evaluate to zero (false).
h Branching out with a GOTO or implied GOTO (a THEN or ELSE <label>). You can
jump from an inner loop to any outer loop with any number of levels in
between, as long as you do not jump beyond the current subroutine level.
h Branching out using the EXIT statement (Chapter 6, Section 3).
h Executing a RETURN statement (Chapter 8, Section 1), which exits not only the
loop but its matching GOSUB, user-defined function or procedure as well.
h Causing an error which is trapped by an ERRSET statement (Chapter 6,
Section 4) at some higher level.
it must be exactly the same index variable defined in the corresponding FOR statement.
This is a formality however and is useful only for programming clarity and style; faster
loop execution actually results without it. An <index variable> is illegal in NEXT
statements that terminate WHILE and REPEAT loops.
There must be one and only one NEXT statement associated with each FOR, WHILE and
REPEAT statement throughout your program. Each loop (FOR, WHILE or REPEAT) many
contain any number of inner loops, which may themselves contain lower level inner
loops. These are called nested loops because each inner loop is completely enclosed (or
nested) within the loop outside it. An error will result if you overlap loops without one
enclosing the other. FOR, WHILE and REPEAT loops may be nested in any combination
and to any nested depth.
NEXT may also be used in an entirely different context: as a line-label. NEXT as a label
always refers to the actual closing NEXT statement of the current FOR, WHILE or REPEAT
loop underway. This can be quite useful in situations when the NEXT statement is not
the first one on the line, for example:
Any control statement that can specify a line number or label may also specify NEXT as a
(pseudo) label, although it is unlikely that it will be used in statements other than some
form of GOTO. An Unexpected Next Error results if the NEXT pseudo label is encountered
when no loop is currently active.
EXIT [<label>]
EXIT statements are used for terminating any FOR, WHILE or REPEAT loop currently in
progress, without waiting for normal loop termination. An optional line number or label
can be specified to tell MegaBasic where to resume program execution after the loop is
terminated. Omitting the line reference causes program execution to resume at the first
statement that follows the closing NEXT statement of the loop terminated. For example:
This loop terminates if the index value increments past 100, or if ARRAY(I)=X. An error
results if no FOR, WHILE or REPEAT loop is currently active. EXIT without the <label> is
a clean, easy method to immediately terminate a loop without using GOTOS.
An EXIT to the pseudo label NEXT (i.e., EXIT NEXT) Will terminate the currently active
loop and begin the next iteration of the loop outside of that. An error results if an EXIT
NEXT is encountered without being immediately surrounded by two or more active
loops.
Multiple loop levels can be exited by repeating the EXIT keyword by the number of loop
levels desired, for example:
This exits three loop levels, then branches to line 150. As you might expect, leaving off
the line reference causes program execution to resume at the statement which follows
the NEXT of the highest level loop exited. Multi-level EXITS are especially useful in
complex nested looping applications where using GOTOS to exit loops is either
undesirable or impossible.
6-18 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
6
With the possible exception of extremely simple throw away programs, real life
programming applications need to be able to tolerate and handle unexpected situations.
Files that were supposed to be there, but weren’t; user’s requesting things that are not
within the realm of possibility; calculations exceeding the bounds of numerical or
mathematical representation. These and many other unforeseen possibilities can and do
arise, and your program must have contingency plans to handle them.
MegaBasic provides several powerful mechanisms to control errors, as summarized in
the table below. These mechanisms have been designed to take advantage of the
structure of your program in a way that lets you handle errors at the appropriate level of
execution in which they arise. This section cover these methods and how best to apply
them.
Most other programming languages handle errors by explicitly generating and passing
error codes around the program. When an error occurs, an error code that identifies the
kind of error is generated and passed to the operation that caused it. If the operation can
deal with this error, it branches off to do so, but otherwise passes another error code
back up to the operation that called it, and so on... The difficulty with this approach is
that the details of passing error codes back from level to level can be very complex and
error prone in itself and requires a great deal of programmer attention to do correctly if
it’s to be done at all.
MegaBasic greatly simplifies this process by automatically passing error codes and
descriptions up to context that actually uses the error description to take concrete
recovery actions. It restores the execution context at the error trap level no matter how
many levels of loops and subroutine calls were piled on top of the error situation. None
of the intervening program code needs to be concerned with error processing at all.
For example the following list of error responses might be assigned to the same error
depending upon which level of the program sees
The MegaBasic ERRSET statement provides independent error control at every level of
program execution. Each GOSUB, user-defined function or procedure, loop or CASE-block
may independently control its own ERRSET traps without affecting traps set by higher
levels of your program. If a lower level subroutine does not set any traps of its own,
traps defined at higher levels will control the lower level errors.
Such a transfer out of a lower level to a higher one is fully supported and constitutes the
only legal and viable way to bypass the normal RETURN mechanism. For example if
GOSUB 100 sets a trap then calls GOSUB 200 which in turn generates an error, the original
trap is used. However if GOSUB 200 sets its own trap before the error was encountered
then that trap is used. Upon RETURN, GOSUB 100 is still protected by its original trap,
unaffected by any ERRSETS within GOSUB 200.
What this means for applying ERRSETS is that the error trap line must be a line at the
same program level as the ERRSET statement that assigns it. Specifically, avoid
assigning error traps that can jump out of the current GOBUS, user-defined function or
procedure that contains the ERRSET itself. Instead, assign a trap to a line within the same
structure. A useful way to understand and remember this concept is to think of an
ERRSET as a special form of GOTO. If an ERRSET were replaced with a GOTO to the same
line and that GOTO is legal (i.e., it doesn’t jump out of the subroutine) then the ERRSET is
also legal and proper.
For example, to transfer control out of a GOSUB when an error occurs within the GOSUB,
execute the ERRSET statement prior to entering the GOSUB, with an error trap referring
to a line also outside the GOSUB (at the same level as the ERRSET statement). This is
actually the most straight forward and easily debugged method for constructing error
traps within a procedure-oriented language of any type. MegaBasic cannot enforce these
rules when the ERRSETS are made, so you must be careful to apply them properly.
When an error trap occurs, MegaBasic restores the entire state of program execution that
existed when the trap was set. This means that the current context of local variables at
whatever depth in subroutines is lost and the original state, as of setting the trap, is
restored. This provides an air-tight mechanism for error recovery that is repeatable in all
situations.
Furthermore, MegaBasic restores the error trap in effect at the time the current
subroutine was entered, rather than disabling the entire error recovery system after an
error occurs. Errors which occur before another trap is set are trapped at the higher level.
In other words, if you set an error trap before entering a subroutine, your program will
remain in control even if the subroutine fails to trap its own errors. An ERRSET
statement without arguments will also restore this higher-level trap instead of nullifying
the recovery system.
6-20 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
6
If PARAM(21) is set to non-zero, high-level error reporting mode is enabled for the
MegaBasic package that PARAM(21) was set in (Chapter 9, Section 5). In this mode,
untrapped errors are not reported as occurring within that package. Instead, the errors
are reported to have occurred in the reference that called the routine in an outside
package.
Up to three diagnostic variables can be specified, separated by commas, and you can
omit them from right to left. Their meaning is positionally defined as:
h line number where the error occurred,
h error type code of the error, and
h the error message string.
These variables are supported for compatibility with earlier versions of MegaBasic. They
are not necessary in new programs because several system functions are supported that
return error information in even more detail, as described below:
You need to use these functions very soon after an error occurs because their values can
change if another error or a Ctrl-C occurs before you access them. In addition to its line
number, the relative statement numbers on the line is useful after an error on a line with
many statements and ERRLINE(l) returns this number. Statement numbers are not
available from compiled programs, where ERRLINE( ) with any argument returns the
same value as ERRLINE with no argument. In compiled programs, you can determine
the relative statement number by reporting error addresses instead of line numbers and
looking in the .map file to figure which line and statement the reported address
corresponds to.
When an error occurs and an error trap is defined, the following sequence of actions
takes place:
h Restores the program execution context of the most recent ERRSET, exiting all
levels of subroutines and loops active since that ERRSET, and restoring
parameters and local variables as if the intervening subroutine levels returned
normally.
h All information about the error is made available to the system error functions
and stored in any <diagnostic vbls> specified in the ERRSET statement.
6-22 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
6
h Restores the prior ERRSET in effect before the current subroutine or loop level
was entered. If no prior high-level error trap was in effect, no trap is defined and
subsequent errors abort the program.
h Program execution restarts at the first statement on the line specified for error
recovery (i.e., the <trap label> given in the ERRSET statement). Your program
has now regained control.
Specifically, an error trap set within a subroutine, loop or CASE block remains active only
as long that program structure is active. After that, the previous error trap, if any, regains
control over errors. The important points about ERRSET error trapping are summarized
below:
h ERRSET traps within loops (FOR, WHILE and REPEAT), CASE statements,
GOSUBS, functions and procedures are active only as long as that program
structure remains active.
h When an error occurs, the trap taken is the ERRSET trap defined at the lowest
program level that is at or above the level on which the error occurred. All active
program structures between the ERRSET level and the level of the error are
terminated (no matter how deep), and execution then resumes at the line
defined by the ERRSET statement. At this point, any ERRSET traps defined at
higher levels will still be active.
h An ERRSET trap can be changed or disabled only by executing another ERRSET
statement at the same level as the earlier ERRSET. For example, from inside a
FOR..NEXT loop, you cannot change or disable an ERRSET trap set before the
loop was entered. An ERRSET statement with no arguments will disable the
ERRSET trap at the current program level.
h ERRSET traps defined within loops last only as long as the iteration in which
they were defined. At the end of each iteration, the error trap active before the
loop began is always restored. If an error trap must be active within a loop
throughout all iterations, its ERRSET statement must be invoked at the start of
every loop iteration.
h All active ERRSET trap levels are displayed by the TRACE RET command, along
with loops, CASE statements and subroutine invocations.
ERRSET traps are very fast, due to the fact that the process of resolving an error location
into a line and statement number is deferred to the point where this information is
actually needed (i.e., in ERRLINE references, setting the ERRSET recovery line number
variable and displaying built-in error messages). Often this location is not needed (e.g.,
as when basing a decision on merely the presence or absence of an error), so such
ERRSET traps proceed much faster. A typical example of this is using the VAL( ) function
to determine if a string represents a valid number.
To assist the program development and debugging process, MegaBasic does not trap type
10 errors when programs are RUN from the MegaBasic command level. Type 10 errors are
those involving errors in program formation, e.g., syntax errors, loop construction, etc.
Such errors need to be exposed during program testing and not hidden by the error
processing mechanisms, as they would be if they were trappable errors. Such errors are
always trapped when the program is run from the operating system command level.
6-24 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
6
RETRY procedures only work for some errors and they require support by the host
operating system to inform MegaBasic of the various error conditions from which
recovery is possible. Multi-tasking operating systems such as MP/M-86, TurboDos-86 and
Concurrent CP/M are among the operating systems supported. MS-DOS partially
supports RETRY and CP/M 86 version 1.1 does not provide any support. Multi-tasking
operating systems provide temporary locks on system resources (e.g., disks, files,
printers, records within files, etc.), which are supported by MegaBasic. If your program
attempts to use any locked resources, it must wait until those resources become available
(unlocked). The RETRY facility in MegaBasic is designed to provide a simple and
effective means for synchronizing with such events. The following errors may be retried
(see Appendix A for further details):
RETRY defines the retry procedure as the name specified, or disables the retry procedure
currently defined if no name is specified. RETRY may be used at any time to redefine the
current retry procedure in effect. Like ERRSET, RETRY is local to the current subroutine
level and therefore when a RETURN statement is executed, the retry procedure defined
(or not) at the calling level is restored and back into effect. Hence subroutines have the
freedom of defining their own retry procedures as required without upsetting those
defined at higher program levels.
The procedure name specified must be the name of a known procedure which does not
have any argument list in its DEF statement. When the procedure gains control after a
retriable error, the ERRTYP system variable will contain the error type being trapped, the
system string variable ERRMSG$ will contain the error message phrase, and the RETRY
function (i.e., not the statement) will return the number of retries that have taken place
on this particular error. To retry the operation that caused the error, just RETURN
normally from the procedure with a RETURN statement.
The retry procedure must not perform any file operations of any kind when a file
operation is the cause of the retry procedure call. To do so will very likely lead to a
corruption of the subsequent retry and cause potential damage to any file currently
OPEN. It should also avoid doing anything which could lead to the same error that
invoked it because RETRY procedures are disabled while one is currently active. Your
retry process may find it useful to use the WAIT statement (Chapter 6, Section 4) to
generate timed delays before resuming with retries. This statement depends on the wait
function only provided by multi-tasking operating systems and not supported in all
single user systems. Retry procedures should restrict themselves to using the RETRY
count and the ERRTYP code to decide on when and how to inform the user, and when to
retry and when to abort.
If after some number of retries you wish to give up, you must execute an ERRSET #
<er ror type> statement to generate your own custom error (described earlier). This is
necessary because you can exit a MegaBasic subroutine only by RETURNing or
generating an error. In the event that no retry procedure was in effect, an error would
have been generated anyway, so this response is consistent and justified. If the program
has an ERRSET trap in effect, the generated error will be trapped and execution will
continue. If no trap is active, then the program will terminate with the error.
The following example illustrates these concepts using the Not Ready Error generated
when a report printout is attempted:
100 Retry INFORM; Rem -- define retry procedure
120 Gosub PRINT_REPORT; Rem -- call the report generator
800 Rem -- Retry procedure for not ready errors
810 Def proc INFORM
820 If errtyp<>25 then Errset #errtyp,“Improper Retry
830 If retry>20 then Errset #25,“Not Readyn; Rem up tO 20 retries
840 Print “Printer not available, type any key when fixed:n,
850 V$ inchr$(0); Return; Rem -- Retry operation
860 Proc end
A retry procedure should handle all possible errors that invoke it by using the ERRTYP
code to branch to one of various routines for each type. When the retry procedure
RETURNS, it resumes the internal process that was in progress, right where it left off,
rather that restarting the MegaBasic statement that lead to the error. Because of this, the
recovery process is invisible to the statement that lead to the error, eliminating the
possibility of errors introduced by restarting actions already partially complete.
In all situations where retry procedures are useful, ERRSET traps could also be
employed. However to use ERRSETS for such purposes, you would have to speically
program an ERRRSET trap for each statement that contains potential for retries. A
RETRY procedure is normally defined once in the initialization of the program and
generally requires no further attention. Furthermore, retries controlled by ERRSETs
necessarily involve restarting the offending statement from the beginning, even though
it might have been partially completed. This requires that such recovery methods be
carefully programmed to ensure that such statements will yield correct results every
time.
6-26 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
6
WAIT<number of seconds>
Generates a time-out delay specified by the number of seconds given. During the
period specified, MegaBasic is not executing and Ctrll-C will not be detected until the
WAIT is finished. The number of seconds may include fractions of a second down to 1
millisecond. Its actual resolution is system dependent but generally the time will always
be within 60 milliseconds of the time specified.
The MS-DOS versions implement the WAIT statement with additional timer accuracy that
correctly resolves to within 1 milllisecond. However, do not rely on the accuracy of any
such timinings when running MegaBasic under Microsoft WINDOWS because the
timing base is unpredictable.
WAIT statements are especially useful within RETRY procedures to slow down the rate at
which retries are performed. For example, one retry every two seconds would be
sufficient to wait for a locked file to become unlocked.
WAIT is supported under multi-tasking operating systems (e.g., MP/M-86 , TurboDos-86
and Concurrent CP/M) and under MS-DOS. Under single-user operating systems it is
supported if the system maintains a running clock with at least one-second interval
updates. If your system does not support the WAIT statement and you need such a
capability, use a FOR..NEXT loop with nothing inside it and an appropriate inside it and
an appropriate iteration count to implement delays.
This section discusses the MegaBasic statements available for accessing data files, for
character device input and output and for interacting with external system processes
and services, as summarized in the table below. See Chapter 2 for the description of the
notation used to specify command and statement formats employed in this section. See
Chapter 9 for all information about additional I/O and system functions.
In all situations where retry procedures are useful ERRSET traps could also be employed.
However to use ERRSETS for such purposes, you would have to specially program an
ERRSET trap for each statement that contains potential for retries. A RETRY procedure is
normally defined once in the initialization of the program and generally requires no
further attention. Furthermore, retries controlled by ERRSETS necessarily involve
restarting the offending statement from the beginning, even though it might have been
partially completed. This requires that such recovery methods be carefully programmed
to ensure that such statements will yield correct results every time.
GFK-0256 7-1
7
7-2 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
7
This section describes the character stream I/O statements and console facilities of
MegaBasic, which are summarized as follows:
General file operations are described in Chapter 7, Section 2, which include a number of
important statements useful in the context of the current discussion (i.e., OPEN, CLOSE
and FILEPOS). Chapter 9, Section 4 provides complete operating details on the built-in
functions related to both I/O and file operations. The functions described there related to
just I/O are summarized below for easy reference:
MegaBasic provides 32 I/O channels through which character streams are transferred to
and from your MegaBasic program. An I/O channel is simply a connection (implemented
in hardware and software) between your program and a device that accepts output
characters one at a time, or provides input characters one at a time. This process is called
streamI/O, because such data transfers usually involve many characters traveling
through the device, resembling a stream of characters. Your computer console screen
and keyboard is a typical example of such a device. Characters are input one at a time
from the keyboard and output to the screen one at a time. Together, this input and
output capability are combined into one I/O device called the console device.
Your program communicates through only one I/O channel at any time and because
there are many I/O channels to choose from, an I/O channel must be selected for use
before any information transfer can pass through it. Therefore each I/O channel is
assigned a unique identifying number called its channel number, which is specified in each
MegaBasic statement or function that performs I/O with the corresponding I/O channel.
Channel numbers range from 0 to 31 and they are defined as follows:
Channel numbers 0,1 and 2 are the built-in I/O channels which are provided by the host
operating system. These channels are always present, assuming that your operating
system has been implemented on your machine to support them. At any time, you can
transfer information between these devices by specifying the channel number 0,1 or 2 in
the MegaBasic command, statement or function requiring the data transfer. The syntax
for specifying channel numbers to MegaBasic facilities will described shortly.
Channel numbers in the range 3 to 31 are set aside for user defined I/O channels. Such
channels must be defined before they are used, with the MegaBasic OPEN statement
(Section 2 of this Chapter). The OPEN statement creates a temporary I/O channel
between your program and an arbitrary device or file already present and maintained
by the operating system, and assigns a channel number to it for use in subsequent I/O
operations. Such I/O channels become undefined and their channel numbers available
for re-assignment once you CLOSE the channel (Section 2 of this Chapter) or your
program ends (Chapter 6, Section 1).
Since channels may be either files or physical character devices, you can redirect output
from your program to storage (files) or onto actual peripheral devices by simply
diverting output to different channel numbers or changing an OPEN statement. The I/O
statements themselves are defined in generic terms which lend themselves to both
device and file I/O without favoring one over the other. It is the channel number itself
that determines the destination of output and source of input to your program.
The actual devices that your program can OPEN are highly system dependent. Under the
CP/M class of operating systems (i.e., CP/M-86, MP/M-86 and CCP/M), there are
unfortunately no devices supported other than the built in devices (0,1 and 2). Only files
can be OPENed on channel numbers 3 to 31 under these systems. However, the MS-DOS
class of operating systems provide a sophisticated facility for attaching arbitraryI/O
device drivers to the system at startup time, which you can access by name, somewhat
like file names. Such devices can be OPENed by your MegaBasic program and used for
I/O purposes just like any other device (or file).
Channel numbers are specified in MegaBasic commands and statements by giving the
channel number preceded by a Ib-sign (#). For example the built-in channels are
specified as #0, #1, and #2. The Ib-sign is needed because such channel numbers are
optional and it informs MegaBasic that the number specified is to be interpreted as a
channel number and not to be confused with other numbers that may also appear in the
same construct. When the channel number is omitted, channel #0 is assumed by default.
Therefore is it not necessary to specify a channel number when transferring data to and
from the console device. A comma separates a channel number from other data or
arguments that follow in the same statement.
7-4 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
7
In commands, channel numbers must be specified with integer constants (i.e., an error
occurs if you specify fractional quantities or numeric expressions in this context).
However no such restriction is placed on channel numbers in program statements,
which can even compute channel numbers using numeric expressions if necessary.
Non-integer channel numbers are truncated to the next lower integer value as they
occur. Channel numbers supplied to I/O functions may also be specified as numeric
expressions, but no Ib-sign (#) should be placed in front of the number, as it is in
statements. Better performance results if you specify channel numbers using integer,
rather than floating point, expressions, but both work.
MegaBasic scans the data list, printing numbers and strings as they come and attending
to format and control specifications as encountered. Numbers and string expressions are
evaluated, then printed in the currently defined format, which may be redefined at any
point in the data list. At the beginning of the PRINT statement, the currently defined
format is the default format, which may also be re-defined at any point.
Normally a carriage return is generated on completion of a PRINT statement, but this
can be suppressed by terminating any PRINT with a comma. This allows several PRINT
statements to contribute to the formation of a single line. Because of how frequently
PRINT statements generally appear in most programs, the PRINT keyword can be
replaced by an exclamation mark (!) for brevity. This notation performs the identical
function that PRINT does.
Format Specifications
Format specifications control the appearance of numbers and strings as they are printed.
For example you may want to be able to control their position on the page, restrict the
number of decimals displayed, select standard or scientific notation, breakup large
numbers with commas, put dollar signs in front of numbers, or justify a string on the left
or right of a fixed-width field. You can specify multiple options with a single format
specification. A format specification appears in PRINT statement preceding the values to
be formatted.
Format specifications consist of a percent sign (%), indicating that a format specifier is
next in the PRINT list, followed by a string expression that evaluates to a format
description string. When MegaBasic encounters a format string expression, it evaluates
the expression and remembers the result to control the format of subsequent items in
the PRINT statement. Nothing is printed when a format string is encountered. Format
strings can be virtually any length, limited only by the available scratch pad memory
remaining (up to 55k). A format specification affects all strings that follow it in the data
list until another format is encountered, or the data list ends.
In most instances, the format string expression will merely consist of a string constant
containing some fixed format, rather than as a large, complex string expression that
dynamically computes a different format based on prevailing conditions. Such flexibility
will be discussed, but for now we shall confine ourselves to the simple static format case.
One such example is as follows:
Print %“c15f2”, X, Y, Z
which prints the values of X, Y and Z with 2 decimal places, commas to the left of the
decimal, and right justified in a field of 15 character positions. Applying this to values
such as 4325, 0.3665, 5893432.567 and 0, the following display would be presented:
The f in the format string is called the format mode character, which selects the type of
format to be applied. There are six numeric format modes and three string format
modes, which are all described on the following pages. Most format modes can be
further augmented by various format modifiers. For example, the c in the format string
above modifies the f-mode to include commas in large numbers. All modifiers will be
discussed shortly.
In the coming pages, we will discuss each of the various format modes and their
modifiers. Then we will show how they can be combined together to form more
complex format descriptions with a minimum of effort. Understanding how to specify
formats involves many things, which you should learn one at a time. Try them out as
you are reading about them; make up your own examples and experiment with them
until you feel comfortable with each concept.
7-6 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
7
Formatting Numbers
If you do not include any numeric format specifications in a PRINT statement,
MegaBasic will display all numbers in the default format. You can designate any format
specification to be the default, but it is normally a special format called free-form, in
which numbers are displayed in the following manner:
h A single space as a separator from preceding values
h A minus sign (–) if negative, but no plus sign (+) if positive
h All leading digits and trailing decimals to full precision
MegaBasic switches to E-notation for numbers that are very large or very small, but most
numbers are shown the way you would normally expect them to appear. Free-form is
useful when numbers are displayed within unformatted text (like words in sentences)
and for quick displays such as those needed during the test and debugging phase of
program development. But free-form cannot be used for numbers that need to line-up in
columns and, many times, it displays numbers with more digits to the right of the
decimal than you would otherwise want.
So if free-form format is not desired, you must explicitly specify a different numeric
format. Numeric formats are denoted explicitly in two forms:
<width> <mode> <decimals>
<width> <mode> <places>
The example format string c15f2 illustrated earlier, specifies a <width> of 15 column
positions, a <mode> character of f (called an F-specification), and 2 trailing <decimals>.
Each of these components are discussed in detail below.
<width>
The format field width specifies a fixed number of print columns (or positions) to use
when displaying the formatted number. This width is specified as an unsigned integer
from 1 to 120, and must be wide enough to accommodate the largest number to be
printed in that field, including all decimals, any decimal point, commas, number signs,
dollar signs, leading spaces and any other characters that will appear in the field.
Numbers are always positioned up against the right-hand side of such fixed-width fields
so that they will line-up when placed in columns. Spaces (i.e., blanks) are used to fill out
the left side of the field to preserve the fixed-width. If you specify a field width that is
too narrow for some number to fit in, MegaBasic will display the field filled with
asterisks instead of the number to indicate a programming error that should be
corrected. However no formal error will be reported so that your program can continue
on.
The field width is optional and if you omit it from a format, the numbers are formatted
with one leading space and however many additional print columns are necessary to
display the number, which may be determined by other specifications in the format
string. Such variable width fields are useful within unformatted text, as in paragraphs
and sentences, similar to free form.
<mode>
The format mode is a single character, in upper or lower case, which selects the type of
numeric format to be used. Six different modes are available, which include fixed
decimal (F), scientific or E-notation (E), integer (I), octal (O), binary (B) and hexadecimal
(H). All mode characters may be preceded by the optional field <width>, described
above, and followed by an optional <decimals> or <places> specification, which are
described below. Each <mode> is individually discussed on the net page.
<decimals>
This is the fixed number of digits to the right of the decimal point that you want
displayed in the number. For example, dollar values with pennies should be displayed
with two decimals. You can specify any number of decimals from 0 to 80 and each
number is displayed rounded to the nearest value with the exact number of decimals
specified. All decimals requested by the format are shown even if they are zeros. This
specification only applies to E and F format modes. Omitting the <decimals> from either
of these modes is equivalent to specifying zero.
<places>
This optional value is placed to the right of the format mode character to specify the
minimum number of digit places to show, even if that means extra leading zeros in front
of the number (which are normally suppressed). It applies only to the integer format
modes (I, H, O and B) and is usually specified to force leading zeros in front of numbers.
Numbers are normally printed with leading zeros suppressed. If you specify more
<places> than you have room for in your fixed <width>, a Format Specification Error will
occur.
7-8 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
7
Format Modifiers
The following set of special format modifying characters may be included within a
format string to produce additional features such as dollar signs, comma grouping (e.g.,
1,435,801), zero suppression, etc. Such modifiers consist of single characters which are
placed within a format specification string to invoke the desired effect.
Several modifiers may appear in a format string for their combined effect; their order of
appearance is of no significance. You can specify a modified free- form format by listing
all the desired modifiers without specifying any format mode (i.e., no mode implies
free-form). Each format modifier is described below:
On F and I format modes, C inserts commas every three places (left of the deci-
mal) after 1000. Remember that these commas take up space in your specified
widths. On the E-format mode, the C-modifier produces a variant of
E-notation known as engineering notation, rather than insert commas.
C The exponent of numbers in engineering notation is always a multiple of three,
and the value portion is a number from 1.0 to 999. Such values are much
easier to comprehend in the same way that numbers with commas are easier to
understand. When using this format option, you must be sure that the format
width (i.e., number of columns) is at least 8 more than the number of decimals
specified. Engineering notation is also used for numbers printed in free-form
comma format for large values requiring a switch to E-notation.
Suppresses trailing zeros to the right of the decimal. Trailing zeros are
changed to spaces (blanks). If the format does not include a width specification
(w), then these spaces will not appear in the field. The Z-modifierapplies
only to the F and E format modes.
Z On string formats (described later), the Z-modifier suppresses trailing spaces gen-
erated on formatted strings. Note that this shortens the field and is thus primarily
useful only on the last string of a printed line.
Indicates positive numbers with a plus sign (+), the same way as negative
+ numbers are shown with a minus sign (–). All numbers will be printed with a
numeric sign, regardless of their value. The + modifier applies only to the F,
E and I format modes.
Positions the sign of the number, if shown, to the right of the number (called a
trailing sign), instead to the left (a leading sign) . The sign will appear as the last
character of the specified field. When applied to fixed-width fields, all numbers
T are shifted over one column to the left to provide room for the sign.
Non-negative numbers in such fields therefore have one space in the last
column of their field (instead of a sign). The T-modifier applies only to F and I
format modes, but it has no effect on negative values when the A-modifier is also
present.
Provides accountingformat for negative numbers, which are shown in
parentheses rather than given a minus sign. When applied to fixed-width
A fields, all numbers are shifted over one column to the left to provide room for
the closing parenthesis. Non-negative numbers in such fields therefore have
one space in the last column of their field (instead of parentheses). The
A-modifier applies only to the F and I format modes.
Suppresses the display of zero values by filling the numeric field with blanks.
N This is useful for enhancing numeric displays that consist of mostly zeros, e.g.,
sparse matrices. The N-modifier applies only to the F, E, and I format modes.
Changes all leading blanks of formatted numbers to asterisks. For example the
* format 15F2$i would format the value 5354.249 as ******* $5354.25. This
modifier has no effect on any of the Hex, Octal or Binary formats and is provided
for use in check-writing applications.
Causes the format specification it is within to become the default format as well as
the current format. This is not really a format modifier since it has no modifying
# effect on the current format. Used alone in the specification, # sets the default
format to free-form numeric output (if it was not already). This may be used
for a particular format that occurs frequently throughout your program. Once
you make it the default format, you never again need to specify it explicitlyin
your PRINT statements, because the default format is used whenever no
format is specified.
Selects the default format as the current format. All immediately preceding format
modifiers are lost, so this modifier should be first when more than one is
D supplied.Additional specifications and modifiers that follow will alter this new
current format as specified. Think of the D-modifier as shorthand for the
default format. The default format is always unmodified free-form unless your
program changes using the Ib-sign #-modifier describedabove.
7-10 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
7
The values after the equal sign specify the ASCII code (in decimal) to use for the
respective usage. Note that P is a modifier used only for changing the decimal point
character and G is used only to specify the number of digits within comma groupings
(C). These changes become permanent for all subsequently formatted numbers within
the same MegaBasic package from which they were reassigned, so to restore them you
have to re-specify their original settings.
The scaling modifier works with all numeric format modes (i.e., I, F, E, H, O, B) and it has
no effect on string formats (i.e., L, M, R). When the binary, hex or octal modes are scaled,
the digits that fall off the end of the number are lost and no error is reported for this.
When any of the decimal modes are scaled, the resulting scaled number must remain in
the range of valid floating point values (a BCD limitation that is not in the IEEE version).
When used, scaling factors should follow the numeric format mode specification. They
could be placed in front, but then a space has to separate the shift count from the format
width that would follow. You can specify a scaling factor all by itself (or with other
modifiers) to scale a number printed in free-form.
PRINT %“8i”,1,%“12f2”,X,%“8i”,J,
%“12f2”,Y,% “8i”,K,%“12f2”,Z
PRINT %“8i,12f2”,1,X,J,Y,K,Z
Although both PRINT statements produce identical results, the later is obviously easier
to type and understand. The individual formats within a multiple format string must be
separated from one another with commas, as shown above. Spaces may be inserted
anywhere within a format string to improve readability but they have no other
significance.
MegaBasic simply applies successive formats from the string to the successive values as
they are encountered and displayed. If the format string runs out of formats before all
the values have been printed, MegaBasic cycles back to the first format in the string and
continues cycling through the formats until all values have been printed. This is called
format rescan. If more formats are supplied than the number of values to be printed, the
extra formats are never used and no error is reported.
You can specify the free-form format in a multiple format string as an empty format, i.e.,
two commas in a row with nothing in between (,,). The default format may be specified
simply as the letter D, a format modifier described earlier.
Multiple formats can be useful even when every number being printed uses an entirely
different format. Shorter data lists result if you define one long format string in the
PRINT statement instead of many separate short ones, and they are generally easier to
read and understand. Long format strings can be built and stored in string variables, so
that subsequent PRINT statements need only refer to their names to apply the multiple
format (e.g., PRINT %FMT$,X,Y,.. )
Multiple format strings have the additional flexibility to intersperse line breaks and
arbitrary character sequences at any point between formatted items. Line breaks and
blank lines can be generated by specifying one or more slashes (e.g., //) as one of the
items in the format string. Each slash generates a carriage return, line feed sequence;
two or more slashes generates blank lines. Slashes are a separate item in the format
string, and as such, they must be separated from other items in the string by commas.
For example, to print the vector X(*) so that 8 values are printed on each line, the
following PRINT statement might be used:
7-12 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
7
In the same way that slashes can be inserted anywhere, you can also insert any string
constant in between formatted items. This is done by specifying a quoted string constant
as one of the items in the multiple format string. Either quote character can be used
(” or ’). For example, to divide the lines generated by the format example above with a
vertical bar ( I ), the following PRINT statement would be specified:
PRINT %“8i,12f2,8i,12f2,’ | ’,8i,12f2,8i,12f2,/”, VEC X(*)
Any characters (or control characters) can be included within such constants except for the
quote character used at both ends. An error occurs if the quote at either end is omitted.
This item in a format string is called a format literal, and it can be used for printing
telephone and social security numbers and other numbers that contain certain
non-numeric characters within them.
When MegaBasic prints a formatted number or string, it first prints any slashes (/) and
format literals that are encountered in the format string until an actual format
specification is encountered. After the last data item has been printed, MegaBasic
generates any slashes and literals that immediately follow the last format specification
(and precede the next specification). The same action is taken if no data items are
specified in the PRINT statement.
Format Repetition
To simplify the construction of more complicated format descriptions, the concept of
format repetition can be applied. In a format string, you can cause any sequence of
format items to be repeated by surrounding the sequence with parentheses ( ), preceded
by the repetition count. For example, the vector print example above can be simplified
using format repetition as follows:
PRINT %“4(8i,12f2),/”, VEC X(*)
where the format sequence ”8i,12f2” is effectively repeated 4 times. Format repetition
can be nested: repetition inside of repetition. For example, the above PRINT statement
can be augmented to print a blank line between every group of five output lines, as
follows:
PRINT %“5(4(8i,12f2),/),1”, VEC X(*)
You can specify up to 5 levels of nested format repetition. MegaBasic reports an error if
you specify more than five, or if the parentheses are not balanced, or if you omit the
repetition count. Properly designed nested format strings can be used to print entire
pages using a single PRINT statement.
Dynamic Formatting
Computing format specifications at run-time, instead of using static fixed format string
constants, is known as dynamic formatting, which can make use of information available
at the time of the PRINT statement in constructing the format string. For example your
format may change depending on how much data is to be printed, its range of values,
and the characteristics of the channel receiving the output. If you wish to print X with
commas, zero-suppression, dollar sign, right justified in a field W characters wide with D
decimals, use the statement:
PRINT %“$ZC”+STR$(W)+“F”+STR$(D),X
Dynamic formatting can be a complex task that requires care and planning. User-defined
string functions are useful here to hide the details of format construction and provide
access to your various formatting processes by name. Functions also collect the format
decision-making into centralized places, confining future changes to a limited area of
your program.
Formatting Strings
Strings are normally printed exactly as given in the data list and additional spacing may
be programmed as needed. Using the wide variety of string operations provided in
MegaBasic, you have great control and flexibility over the format of strings. Formatting
entire displays exclusively with string operations can be a very powerful way to control
the appearance of your output. The STR$( ) function (Chapter 9, Section 3), which
converts a number into a string form, provides all the support necessary for combining
numeric and string information into formatted data ready to display.
There are, however, several simple string formats that are commonly required in many
applications, and hence are provided in MegaBasic: left and right justification and
centering. The format specifications for these capabilities are described below using the
same notational conventions as those employed to describe the numeric format
specifications earlier in this section.
When MegaBasic encounters a number when a string format is specified, the number is
printed in free-form format and the string format is then applied to the next item in the
output list. When MegaBasic encounters a string when a numeric format is specified, the
string is printed as-is (unformatted) and the numeric format is then applied to the next
item in the output list. This allows your program to continue in the face of format type
errors, and lets you insert unformatted numbers and strings into print statements with
minimal affects on the format strings being applied.
When the last item of a print statement is L(eft) or M(iddle) formatted, the line will
usually end with trailing spaces. If you do not wish this to occur, you can specify the Z
modifier to suppress the trailing spaces on any formatted string. Note that this shortens
the field and is thus primarily useful only on the last string of a printed line.
7-14 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
7
Control Specifications
Special control specifications may also appear in the <data list>. These are not format
specifications and are not preceded by a percent (%). A plus sign (+) resets the line
count for the channel to zero before proceeding and is necessary only in applications
where this count is being used with the LINES() function. The plus sign (+) does not
generate any printed characters and has nothing to do with the similar format modifier
(+).
Multiple blank lines may be generated from a single PRINT statement by a field of
slashes, similar to FORTRAN format statements. For example: PRINT #D,///, will
generate 3 carriage returns on channel D. Slashes may be interspersed throughout the
data list.
MegaBasic generates a carriage return at the end of the PRINT statement unless you
suppress it by ending the statement with a comma. For example PRINT x displays X and a
carriage return, while PRINTX, displays X without a carriage return so that later PRINT
statements can continue on the same line.
TAB(P) advances the cursor to column position P prior to printing the next item, where P
is a numeric expression that evaluates to a value from 0 to 255. This is accomplished by
printing spaces until the desired position is reached. TAB(P) is ignored if P is less than or
equal to the current position.
Printing to Files
When printing to channels 3-31, you are really transferring data to a file OPENed under
the same file number. Exactly the same data is transferred to the file as would be
displayed on the console if channel #0 were employed. However it can be important
that the last byte printed before the file is CLOSEd be an appropriate end-of-file mark so
that the file can be processed correctly by other programs. The MegaBasic endmark (an
8-bit value of 26, ASCII CTRL-Z) is placed automatically after each PRINT for this
purpose if enabled. See PARAM(9) to use a different file endmark. You can suppress this
endmark from being written to the file by typing the reserved word NOMARK as the last
item in the data list of the PRINT statement you want it suppressed on. See the NOMARK
statement for other information.
This is appropriate for later INPUT processing by MegaBasic programs, but typical file
processing programs external to MegaBasic sometimes expect other endmarks. For
example ASCII codes 0,1, 26 and 255 are common. You must handle this situation by
redefining the endmark code with PARAM(9) (Chapter 9, Section 5). Note that whatever
code is used cannot be part of the text printed without causing a false end-of-file
condition when later processed.
A simple console INPUT statement that requests a string, two numbers and another
string (i.e., four inputs in all) might appear as follows:
INPUT A$,X,Y,B$
In this example, we omitted the channel number to select the console by default. INPUT
can accept numbers in any form that MegaBasic recognizes as valid numeric constants.
This includes signed and unsigned numbers with and without decimals, E-notation,
octal, binary and hexadecimal. INPUT will not accept invalid numbers and automatically
re-requests a new response from the user for any numeric input that is out of range or
not a valid number or includes decimals on values destined for integer variables. You can
input numbers into several numeric variables with a single input consisting of several
numbers separated by commas or spaces. See Chapter 3, Section 2 for a complete
discussion of numeric constants under MegaBasic.
You can also input a number into a string variable, but in this case, the number is simply
treated as an arbitrary sequence of characters, i.e., no numeric validation is performed.
String variables accept the entire line of input, even if it contains spaces, commas,
numbers or words and phrases.
Input strings larger than the DIMensioned size of the input variable will be truncated
to fit the string. Inputs into indexed string variables (or into string fields) that are shorter
than the region indexed are stored left justified in the fixed-width region, padding all
remaining character positions to the right of the characters input with spaces.
INPUT statements provide all the editing capabilities of the MegaBasic line editor
(Chapter 1, Section 6) for each input. Although you can potentially INPUT data on a
hard-copy terminal (i.e., on paper instead of a screen), the line editor assumes that a
screen is being used. On a hard-copy device the editing process will cause severe
misalignment of characters if any insertions, deletions or backward cursor movements
are attempted.
Input Prompts
An input prompt is a message that is output to an interactive input channel so that the
user knows that an input response is required. They also usually include additional
information about the kind of input expected. The INPUT statement lets you specify an
input prompt in front of any input variable in the <input list>, for example:
You can specify prompts as any string expression that does not begin with a variable or
user-defined function name. This is so that MegaBasic can tell prompts and input
variables apart. If your prompt is in a string variable, you can surround it with
parentheses to specify the prompt variable in an input statement. MegaBasic always
re-displays the prompt when invalid numeric responses are re-requested.
If you do not specify an input prompt for any particular input variable, MegaBasic
automatically provides the prompt message: ? . To suppress any prompt messages,
including the automatic question mark, specify a null string ( ) as your prompt. If the
user types several numbers in a single input response, only the initial prompt for the first
numeric input variable appears; the unneeded prompts are suppressed. For example:
INPUT “1st value = ”,X, “2nd value = ”,Y, “3rd value = ”,Z
7-16 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
7
If, in response to the input request above, you type all three values separated by commas
or spaces in one input line, the second two prompts never appear on the screen and the
program continues on. You could also type one input value, then type two input values,
suppressing only the prompt for variable Z. This lets you skip ahead in an input
sequence that you have been through many times: sort of an expert mode. Note,
however, this only works for numeric inputs, not string inputs, and only within a single
INPUT statement.
In order to maintain the correct column position for the console, the maximum input
string you can enter is limited to 255 characters minus the length of the INPUT prompt.
Therefore, really long prompt strings can prevent you from entering all the characters to
may wish to.
Your program can, of course, store the string Yes or No into the ANSWER$ variable before
issuing the INPUT statement. If the contents of ANSWER$ happens to be what the user
was about to type, the response need only be a single keystroke (i.e., typing the RETURN
or ENTER key). If the response differs only slightly from the contents of the variable, the
user can easily edit the visible entry already present on the screen into the desired
response, before typing the ENTER key. To edit this value, the first key you type Must be
an editing control key, rather than an ordinary input character. If the first key is an input
character, the current default entry shown vanishes from the screen and is replaced by
the key struck. This makes it unnecessary to delete the entry first when all you want to
do is simply type a different entry without editing.
Any input variable can be edited by preceding its name with the EDIT keyword.
Variables without this modifier will be input the usual way. You can edit numeric
variables in this manner as well. Numbers are displayed using the default format
currently in effect (explained earlier in this section) and with all leading and trailing
blanks removed. Do not use a numeric format that inserts commas into large numbers
because that would divide the input into several numbers (e.g., 1,234,567 is interpreted
as three numbers).
INPUT Echoes all input keys typed during each input entry.
INPUT1 Suppresses the carriage return echo after each input.
INPUT2 Suppresses all echo and editing control key action.
INPUT2 works just like the INPUT1 statement except that characters input are not
echoed to the console or other specified channel. Editing control keys are not recognized
and input as normal characters and input is terminated with a carriage return. This is
ideal for the input of passwords or other applications where echo suppression is
desirable. All three forms of INPUT behave in an identical manner when inputting from
an open text file: suppressing the echo of character and carriage return input and
disabling all input prompts.
7-18 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
7
h Carriage returns and linefeeds (ASCII codes 13 and 10) are treated differently when
they appear in pairs. A CR-LF sequence collapses into a single CR code (ignoring the
LF). An LF-CR sequence collapses into a single LF (the CR is ignored and does not
terminate the line).
h An error occurs if you attempt to INPUT a numeric variable and no valid number is
present at the current file position. If numbers in the file contains commas, dollar
signs ($) or other extraneous characters, then numeric input is not directly possible
and you must input such values as strings and extract the values with string
operations. Numbers with decimals input into integer variables are truncated.
An error occurs if your program attempts to INPUT a string when the first character is
the end-of-file mark (26 code) or past the last file byte. You can test for this condition by
testing the INPUT( ) function (Chapter 9, Section 4) for a value of zero before each
INPUT line (Section 1 of this Chapter), or by trapping the error with the ERRSET
statement (Chapter 6, Section 4). To recognized a different endmark code, use PARAM(9)
to redefine it (Chapter 9, Section 5).
Command-Level Arguments
Whenever you execute a program, either from the MegaBasic command level or the
operating system command level, you can follow the command that starts the program
with additional characters on the same line. This sequence of extra characters is called
the command tail, and MegaBasic places it into the old line buffer when program execution
begins so that you can retrieve them (using the EDIT$ function) as needed by your
program, extracting any additional arguments it contains. For example, suppose you run
a program from the operating system using the command:
BASIC Program data1 data2 data3
When Program begin execution, EDIT$ will return the additional data typed as the
string: Program data1 data2 data3. This command tail must be used before your program
requests console input via the INPUT statement, because the edit buffer is then
overwritten and its prior contents are lost. See the EDIT$ function (Chapter 9, Section 4)
for special considerations about accessing the command tail string and using EDIT$ in
general.
7-20 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
7
MegaBasic provides a complete set of file operations that your program can apply to
accomplish any desired file processing task. Most operations refer to files either by name
or by open channel number. Each statement performs a relatively simple operation, all
of which are summarized in the table that follows, giving you a view of the total range of
possibilities before delving into the detailed discussion of each file processing statement.
This section of the manual describes the facilities in MegaBasic for accessing and storing
data on files maintained by your operating system. You should be familiar with your
particular operating system, its capabilities and user facilities for basic file operations.
What is a File?
A file is simply a sequence of bytes or characters stored on some mass storage device,
which is maintained by the operating system as an individual data object, rather than as
separate bytes of unrelated information. Files may be of any length up to some limit
imposed by the physical size of the storage device and the configuration of the operating
system. Dividing large amounts of data into a set of files permits easier handling of the
data, similar to the way that manual systems break up data into individual files
containing related information.
Since many files can be maintained by the operating system, each one is assigned it own
name, unique from the rest, so that any particular file can be identified when it is needed
for later access. File names are assigned by people and by computer software when the
file is created for the first time, and the name assigned usually contains some indication
of the purpose of the file or its contents. File names are expressed in MegaBasic
programs using string expressions, and in commands as a series of characters without
quotes around them. Consult your operating system manual for details concerning file
names, file types and internal file structures.
Accessing Files
In MegaBasic, as in other programming languages, a file must be opened before your
program can access its contents. This is similar to manual systems, where files must be
withdrawn from a file cabinet drawer and opened to view before the data they contain
can be accessed. Opening a computer data file causes MegaBasic to build a number of
internal control structures that provide efficient, direct access to the file without having
to give the file name for each operation. Your program can have up to 32 files open
simultaneously. An open file is identified in your program by its open channel number,
an integer from 0 to 31 which is assigned to a file when it is initially opened.
Once a file is open, you can read from it, write to it, determine or change its size, find out
the date and time that it was last modified, etc. Since a file is just a sequence of bytes on
a storage medium, the location of any particular byte in a file is called its byte position.
The first byte in a file is always at byte position 0, the second byte is at position 1, and so
on up to the last byte of the file. Byte position numbers are important because all data
transfers occur at specific byte positions in the file. You have to specify file positions to
access data located at random locations scattered about your file.
7-22 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
7
Record-Oriented Files
In some applications, it can be useful to organize a file as a sequence of fixed-length
records, where each record consists of a set of data fields. In MegaBasic, you can
implement this by reading and writing records as fixed-length strings (see the READ and
WRITE statements about this). Such strings can be built from or divided into their data
field components using either standard MegaBasic string processing methods, or by
accessing the record string as a structured variable, composed of the desired data fields,
then accessing the various fields directly as variables. The subject of structured field
variables begins in Chapter 5, Section 3.
The optional <drive code> is a letter indicating the physical drive on which the file
resides. You can omit this if the file resides on the default drive (see your system manual).
A colon (:) separates this letter from the rest of the name when the <drive code> is given.
The optional <pathname> specifies the directory where the file resides. The <primar y
name> is mandatory and consists of 1 to 8 characters. The optional <extension> consists
of 1 to 3 characters and immediately follows the <primar y name> with a period (.) in
between for separation.
A file name cannot contain question marks (?) or asterisks (*) for matching multiple files.
Periods (.) and colons (:) can appear only as separators as shown above, never within the
name portions. All letters in file names may be typed in upper or lower case for the same
effect. Also, no blanks (spaces) may appear anywhere within a file name; those in the
above file name format exist solely for visual purposes and do not appear in actual file
names.
Under MS-DOS you can specify file names with their directory path. This provides access
to files in directories other than the currently selected directory. As with file names, path
names can be typed in upper or lower case, but MegaBasic converts any lower case
characters to upper case internally. The names within a pathname must be separated by
backslashes (\) or forward slashes (/), and MegaBasic converts forward slashes to
back-slashes before the name is used internally.
Any legal MS-DOS pathname is acceptable to MegaBasic. Hence the file .. \ x refers to the
file named X in the directory just above the current directory. See your MS-DOS
operating system users manual for complete information about file pathnames and how
to specify them.
File Functions
There are a number of built-in MegaBasic functions (Section 2 of this Chapter) that
provide information about files useful to your file processing applications. These
functions are summarized below for quick reference.
7-24 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
7
Under MS-DOS, RENAME lets you rename a file with a different directory pathname,
causing the file to be physically relocated to that directory without any time-consuming
copy transfers. Such moves can only occur between directories on the same drive: a File
Creation Error is reported if you attempt to move a file across drives or network nodes.
This statement also changes the current drive, as needed, in addition to changing the
current directory path. You can change the drive without changing the directory path by
specifying only the drive part, e.g., ”D:” or ”C:”. Prior to MegaBasic version 5.60, DIR$-
did not modify the current drive, requiring you to set PARAM(2) for that purpose. If you
want to set the current directory on another drive without changing drives, you now
have to save and restore the current drive using PARAM(2).
When used as a string function, DIR$ (Chapter 9, Section 4) by itself (without any
arguments) will return the current pathname for the default drive. With an argument,
DIR$() Wi11 extract file names from the directory for further use in directory scanning
applications.
DIR is provided so that your MegaBasic programs can list file directories; the following
examples illustrate its use:
7-26 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
7
Optional <modifiers> may be specified to control file access in the following ways:
The file is OPENed in a mode that allows other users on the machine
to also OPEN the same file simultaneously. Under multi-user
environments, MegaBasic provides an automatic record locking and
SHARED unlocking mechanism to ensure file update integrity. Under single-user en-
vironments, such transfers involving such files are unbuffered, resulting in
much slower response but immediately posting file changes to disk. We will
fully discuss the use of OPEN SHARED files later on beginning in
Chapter 7, Section 2.
Only permits READ or INPUT operations to be performed on the open file.
INPUT
A WRITE or PRINT to such files causes a Read-Only error. In
(or multi-user systems, OPEN INPUT also allows other users to also have the
READ) file open, but no locking operations are supported unless you also open
the file SHARED.
Opening a file for APPEND under CP/M systems may be a dangerous thing to do,
because there is no way to know exactly where the end of the file really is. CP/M
maintains the size of a each file in units of 128 byte blocks and unless you are always
writing 128 byte records, opening for APPEND Will usually position the file farther than
the actual end of the data.
If no modifiers are specified, the file is OPENed for input and output, and private
(exclusive) access under multi-user systems. The SHOW OPEN command (Chapter 2,
Section 5) shows you the current characteristics of all OPEN files.
The same file may be OPENed under more than one channel number so that several file
pointers can be independently used in subsequent file operations. This can be quite
useful in situations where sequential access from several different locations occurs
simultaneously, such as copying data from one location to another or sorting. MegaBasic
controls multiple buffer usage so that at all times any given 512-byte file segment can
only be buffered by at most one buffer. This is transparent to the program and prevents
any file update problems due to the multiple buffers.
A typical application of multiple file-OPENS is reducing record sizes in a file, in-place,
rather than creating a separate result file. For example the following program removes
all the spaces from a text file:
10 Rem –– Remove spaces from file F$
20 Open #8,F$; Open #9,F$; Dim LINE$(200)
30 While Input(8); Input #8,LINE$
40 LINE$ = LINE$-“ ”; Print #9,LINE$; Next
50 Close; End
Line 20 opens file F$ once as an input file (#8) and once as an output file (#9). Line 30
processes each line in the file until the end of file is reached. Line 40 removes all spaces
from the line input and PRINTS the resulting line back to the file. The multiple buffers
and separate file pointers make this process both easy to program and very fast. This
kind of process only works when each resulting line is no longer than the input line.
Files OPENed under channel numbers above 2 may be sequentially accessed by PRINT or
INPUT statements and by READ and WRITE statements. The example program shown
earlier does all file access with PRINT and INPUT. This facility provides efficient
sequential access to text-files. See Chapter 7, Section 1 for the details (under PRINT and
INPUT).
All OPEN files, their channel numbers, current file positions, and other characteristics are
accessible and common to all programs in a multiple package system. In such a system, it
may be useful to have a central routine manage the set of channel numbers which is in
use and available. The OPENS(F) function can be employed to test a channel number for
availability.
OPENC is often much more convenient to use than OPEN, but at the expense of exposing
data files to possible erasure from unintentional misuse. OPEN is more conservative than
OPENC, but the files it opens must always exist and it performs no extra services.
7-28 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
7
For example:
Closes all files currently open. Avoid this form when there is a
CLOSE possibility that you might unintentionally close files opened by
other packages.
CLOSE #N+3 Closes the channel number specified by the expression N+3.
MegaBasic does nothing if you specify a channel number that is not currently open. All
files are automatically closed when your program ENDS, but they will remain open after
a STOP statement so that subsequent CONTinuation can proceed with the same files
available.
Real Variable
Reads a floating point value from the file into the specified real variable. No validation of
the bytes read is performed; whatever bytes are present in the file are read into the real
variable. Hence, a real (memory image) representation is read from the file, instead of
the less efficient ASCII representation (as some other BASICS read). INPUT statements
should be used for reading ASCII numbers from files. The number of bytes read depends
on the floating point precision provided by the current version of MegaBasic. Chapter 3,
Section 1 discusses the internal representation of floating point real numbers. Chapter 3,
Section 7 describes how to read entire arrays from the file in one READ.
Integer Variable
Reads the next four bytes from the file into the integer variable specified. These bytes are
ordered from the low to high in the file. No data type checking is performed on integer
values as they are read from the file (as done for string values described above).
Chapter 3, Section 7 describes how to read entire arrays (i.e., vectors) of integer values
from the file in one quick operation.
7-30 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
7
This method gives you the most control over how many bytes are read, but you are
responsible for controlling the length of the transfer by controlling the length of the
receiving string variable. In general, fixed-length strings are the best way to read
fixed-length records from a file. Additional steps are required to access the individual
data fields within such records.
Reading Numbers
The numeric precision assumed when floating point values are read from a file must
agree with the precision in effect when the data was written. For example if a file was
written using 14-digit precision MegaBasic, it cannot be read by any program being run
under 8,10 or 12-digit configurations of MegaBasic. The precision of floating point values
read/written to files is, however, independent from the precision used for variables.
PARAM(11) may be set to any precision from 6 to 18 digits to control subsequent file
transfers (p. [P#,param).
Binary data can also be read into numeric variables by prefacing each numeric variable
reference with an ampersand (&) for 8-bit values or an at-sign (@) for 16-bit values.
16-bit values are defined as the next two bytes on the file with the low-order byte first.
Both 8-bit and 16-bit values are interpreted as unsigned integer values. Binary file
operations bypass all type checking since they read whatever is presented to them. The
8-bit and 16-bit values are converted to floating point format when read from the file
into real variables. Integer variables do not require this conversion and hence a much
faster transfer results. The following READ statement illustrates the possibilities:
READ #F,X,A(I,J),8Y,@B(K)
where numeric values are read into X and A(I,J); a byte-value is read into Y and a 16-bit
word-value is read into B(K). Reading vast quantities of bytes with this method is not
recommended because each byte read is individually read and dispatched to each
destination variable one at a time. Also, you should avoid using real variables for
receiving 8 and 16 bit values because of the considerable effort that is required to convert
each value to floating point representation, a process performed internally for each
variable read. Large quantities of binary information should be read as fixed-length
strings into string variables, as described shortly.
Reading Strings
String variables can be read as a whole or as indexed sub-strings. String data is written in
a special compact format: the amount of storage taken equals the length of the string
plus two for up to 255 characters, or plus three for over 255 characters. The short
sequence of bytes in front of such strings is called the string header, which tells
MegaBasic that a string of some specified length resides on the file. This header makes it
possible to sequentially read variable length strings, one after another from the file,
without having to explicitly specify and control how much data to read on each transfer
(because the length is embedded in the string itself).
If the string on the file is larger than the variable it is read into, the characters that don’t
fit are lost just like string assignment statements). Regardless of the string variable
capacity, the file pointer is always set properly to the next item in the file after reading
any string.
In some applications, string headers are not suitable because you are reading pure
binary information from the file that was not written as variable-length strings. An
example of this is a file written by a foreign system which your program is processing by
interpretation. To read pure sequences of bytes from files, bypassing all type and header
controls, precede each string variable name with an ampersand (&). The number of
bytes read is controlled by the current (before the READ) length of the string. 16-bit
binary READS are not possible with string variables. The following example shows how
this is done:
10 DIM A$(100), B$(167); Rem––Set your string sizes
20 Read &A$, &B$; Rem –– Read 100 bytes into AS, 167 into B$
30 Read &A$(1,N), &B$(1,M); Rem––Read N bytes into A$, M into B$
It is very important to realize that the number of bytes read using ampersand string
variables is equal to the length of the string variable specified in the READ list. This
length is not necessarily the same as the DIMensioned size of that string variable. For
example if we set A$=”” prior to the READ in line 20 above, zero bytes will be read into
A$ when it is read. Furthermore, this kind of READ never affects the length of any string
variable, as measured by the LEN function (e.g., LEN(A$)). A length of a string variable
can be set to any length up to its dimensioned size with a statement like: LEN(A$)-N, as
described in Chapter 5, Section 1.
Random-Access READs
As each data item is read from the file, the file position is advanced by the number of
bytes read, so that the file position is always aligned to the next data item. When
randomly accessing a data file, you must specify file positions which always refer to the
first byte of multi-byte data items (such as strings, 16-bit values, and floating point
values. To do this you must know the number of bytes required for each data item.
String and binary data types are covered above. The length of floating point values is
always the same for a given MegaBasic precision: PRECISION/2+1. Thus the standard
8-digit precision requires 5 bytes (8/2+1 = 5). If you ever access a data item somewhere
past its first byte, a Data Type Error will usually occur to inform you of the problem.
However binary data items have no identifying characteristics to permit such error
detection, so exercise great care when processing random binary files.
7-32 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
7
Variable-length Strings
Writes the string specified as a variable-length string to the file at the current file
position. Such strings are written to files with a two or three byte header that specifies
how long the string is, so that the same length can be read back at a later time. The
header also helps MegaBasic verify that a string is actually on the file when a READ
request attempts to read it. Variable-length strings are useful for variable-length record
structures.
Real Values
Writes the specified floating point real value to the file at the current file position. The
exact memory image of the (IEEE binary or BCD) floating point value is written to the
file, rather than a printable ASCII numeric representation (as written by some other
BASICS). The number of bytes written is dependent on the floating point precision
provided by the current version of MegaBasic (as described in Chapter 3, Section 1). The
precision of floating point values read/written to files is, however, independent from the
precision used for variables. PARAM(11) may be set to any precision from 6 to 18 digits
to control subsequent file transfers (Chapter 9, Section 5). PRINT statements should be
used for writing ASCII numeric representation to files.
Numbers should be written to files in this manner when speed is important and the
numbers being written contain decimals or can span a very large numeric range. This is
the most general purpose numeric file format. A number is written in this representation
whenever the number evaluates to a real number. See Chapter 7, Section 3 for complete
details on the differences between real and integer numbers and how to specify them.
Chapter 3, Section 7 describes how to write partial or entire arrays to files in one quick
step (i.e., as vectors).
Integer Values
Writes the specified integer value to the file at the current file position as a sequence of
four bytes. These bytes are ordered from the low to high in ascending locations on the
file. You should consider this numeric file format when speed and wide range are
important and the values being written are always integer, especially those which are
manipulated within programs in integer variables. This representation is particularly
well suited for storing file position pointers used in complex linked file structures. A
number is written in this representation whenever the number evaluates to a integer
number. See Section 3 of this chapter for complete details on the differences between
real and integer numbers and how to specify them. As with real numbers, integer arrays
can be written to files using vector write operations, described in Chapter 3, Section 7.
7-34 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
7
This statement writes R+S and LOG(X) at the current file position, X and Y at position
Pl, and Z/Y-54 at position P2. Without any file position specifications (%), all data is
written sequentially on the file. Writing past the physical end-of-file causes the file to be
extended automatically.
Writing Numbers
It is of the utmost importance for you to understand that numbers are written to the file
in different ways depending on whether the number evaluated in the <data list> as a
real number or as an integer number. Integers and reals are written using a different
number of file byte positions and their structures are interpreted in totally different and
incompatible ways. Programs that expect a certain numeric representation on a file
where a different one resides will produce erroneous and unpredictable results. You
should be familiar with all of the material presented in Section 3 of this chapter, in order
to properly express numeric values and computations so that they evaluate to the
expected numeric type.
However, you can specify an optional <type mode> for specific WRITE (and READ)
statements to force numbers being written to a specified type, regardless of how they
are specified. Either of the reserved words REAL or INTEGER may be inserted between
the WRITE (or READ) reserved word of the statement and the <channel>, to cause any
particular file transfer to only write the numeric file representation corresponding to the
type specified. Numeric type control only affects numbers and has no effect on strings,
even if the string contains numeric structure fields.
If a number appears in the <data list> of the wrong type, MegaBasic automatically
converts it to the <type mode> specified. Although too many such conversions can
degrade the performance of your program, your program will transfer numbers in a
predictable and expected manner. You should be aware however that these conversions
have certain limitations on numeric range and precision that are also described in
Chapter 3. The following two example WRITE statements illustrate an integer-only
WRITE and a real-only WRITE to the channel number contained in variable F:
Write integer #F, X,Y, Z
Write real #F, X,Y, Z
Binary data can be written by prefacing each numeric expression with an ampersand (&)
for 8-bit values or an at-sign (@) for 16-bit values. Floating point values from 0 to 65535
are converted to binary format before the WRITE takes place. The optional <type mode>
has no effect on any binary format. For example:
Write real #F,X,A(I,J),&Y,@B(K)
In this WRITE statement, X and A(i,j) are written to the file as floating point values, Y is
converted to an 8-bit value and then written, and B(E9 is converted to a 16-bit value and
then written. 8-bit and 16-bit values consume only 1 and 2 bytes of file space,
respectively.
Writing Strings
String expressions may be included in the data expression list and are written in a
manner corresponding to the READing of strings. Preceding string expressions with an
ampersand (&) causes the string to be written as a sequence of fixed-length bytes for the
length of the string. Without the ampersand (&), strings are written with a 2 or 3 byte
header in front of the string for identification purposes; ampersand (&) strings are
written as pure fixed length byte sequences without any header. The following example
illustrates the various ways a string can be written:
Write #F, A$,B$(1,J),“literal”+Q$, &C$,&D$(K,L),&Chr$(0)*15
This statement writes A$,B$(i,j)and”literal”+Q$tofile#Fasvariable-length strings,
followed by fixed-length strings C$, D$(k,1) and CHR$(O)*15 (without headers), the
later of which generates a sequence of 15 binary zeros, a topic described in Chapter 4,
Section 4.
Since the elements of a WRITE statement may be general expressions, each data item
listed in the data list is evaluated internally and this internal result is then written to the
file. String data can potentially consume all the internal work space set aside for this
purpose and hence great care must be exercised when very long computed strings are
being written.
7-36 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
7
In the case where the string expression consists solely of a string variable (indexed or
not), the contents of the variable are transferred directly to the file, rather than being
evaluated internally as a general expression. Hence no internal memory is required for
this common case. A side benefit of this implementation is that large transfers out of
string variables proceed twice as fast as transfers of string expression results of
comparable length.
End-of-File Marks
If file endmark generation is enabled (see the NOMARK statement below), MegaBasic
writes an additional single byte file endmark at the prevailing file position (without
advancing the file pointer) after executing each WRITE statement. While useful for
purely sequential file usage, this often proves unsatisfactory for binary or random access
operations. To prevent the generation of the end-of-file mark, you may finish WRITE
statement with the NOMARK keyword, for example:
Write #F, A$, X, Y, Nomark
This same keyword may also be used as a program statement to provide global control
over the generation of end-marks for subsequent write operations (see the next
statement) . The endmark code generated may be redefined using PARAM(9), described
in Chapter 9, Section 5.
7-38 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
7
MegaBasic makes use of the record locking capabilities of the multi-user system to give
your program temporary exclusive access into critical areas of the SHARED file (regions
where potential modifications could disrupt the intended sequence in progress).
Although this locking mechanism has been designed to be as automatic and transparent
as possible, there are inherent difficulties with the very concept of shared files which
require the MegaBasic programmer to fully understand the actions invoked by READ (or
file INPUT) and WRITE (or file PRINT) statements.
LOCK and UNLOCK give you complete low-level control over the locking used on SHARED
files. They do nothing if applied to files not opened in SHARED mode. However, you are
responsible for unlocking everything you lock in exactly the same manner it was locked
and for remembering to do so before closing the file. LOCK and UNLOCK affect neither
the current file size nor its position for subsequent READS or WRITES. Retriable
Suspended File Access errors are generated if the file is already locked by another process.
The automatic locking performed by MegaBasic on SHARED files is still active and can be
used along with the LOCK and UNLOCK statements. However, since these direct locking
statements provide more control over locking, PARAM(26) can be set to 1 to disable the
automatic locking of MegaBasic (or to zero to re-enable it again). PARAM(26) is local to
each MegaBasic package. Setting PARAM(22) to –1 will disable automatic locking over all
packages in the application.
In multiuser and local area network applications, record locking is generally provided by
letting an application inform the operating system that one or more regions of a file will
be temporarily locked to prevent other users from accessing them while they are being
changed. After completing the transaction, the user unlocks the locked regions and
continues on.
In a record-oriented file model, records can be locked and unlocked as they are accessed.
But a different approach must be used in a stream-oriented file model because the
records are conceptual, rather than physical. Since the operating system typically limits
number of individual file locks per process (usually less than 64), locking each byte is not
feasible. Instead, MegaBasic defines records, for record locking purposes, in the
following way:
h The beginning of a record is defined as the first file byte accessed by a READ or WRITE
statement or the first byte accessed after a change in file position.
h The end of a record is defined as the last byte accessed by the READ or WRITE
statement before the end of the READ or WRITE statement, or the next file position
change, which ever occurs first.
Thus each READ and WRITE statement can access one or more records, delimited by the
statement boundaries and/or file position changes. For example, the following READ
statement accesses three records:
READ #F, A$, %123,X,Y,B$, %9999,C$,Z
Notice that several variables may be accessed in one record and that you could read the
beginning of a record without reading the rest of it. It is important to realize that this
notion of records is defined for the purpose of record locking and it may not correspond
to the logical records that a program uses when accessing the file.
When your file is OPEN SHARED, all READS and WRITES (INPUTS and PRINTS too)
implicitly lock and unlock the file blocks in a predetermined way. Your programs are
never concerned with the actual lock/unlock operations themselves. Your program is
strictly concerned only with its READ and WRITE statements. Internally, MegaBasic
performs READS and WRITES in the following sequence of steps:
7-40 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
7
In other words, a WRITE statement locks each record before writing it, and writes each
record directly to the file without buffering. On completion, all records currently locked
are unlocked, including both the newly locked records and any records already locked
before the write operation began.
Notice that WRITE is the converse of READ in all respects. In most programs most of the
time, the scheme for READS and WRITES as defined above will provide all the
locking/unlocking features necessary. For example reading some values, using them in a
computation, then modifying and writing them back to the file will always operate
correctly, even if other processes are trying to do the same thing. This policy also tends
to minimize the number of file locks active at any given instant.
At first glance, it may appear that locking only one byte of a record would not be
sufficient for reliable operation. However, shared access is an inherently cooperative
process that would not work without agreement by all competing processes. As such,
locking the first byte of a record is equivalent to locking the entire record as long as
every file access abides by one very simple rule: always access records from the
beginning of the record, i.e., do not access the middle of a record without accessing the
beginning. This is a very reasonable rule that is easy and natural for any process to
observe. Such processes already have to cooperatively access files through the locking
primitives provided by the operating system, so one additional minor rule is in no way
unreasonable.
Some operating systems, notably Concurrent CP/M and TurboDOS 86, cannot lock files
down to the byte level. Instead, they only support locking of file blocks, typically 128
bytes each. Therefore when MegaBasic locks the first byte of a record under these
systems, it is actually locking the appropriate block in the file that contains the desired
byte. This can sometimes cause unexpected delays due to the locking of nearby regions
unrelated to the transaction at hand. This cannot be avoided in such systems.
7-42 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
7
These statements provide access to system memory and hardware ports 0 to 65535 and
permit control of various MegaBasic system parameters. See Chapter 9, Section 5 for the
discussion of additional functions FREE(), EXAM(), INP() and variable addressing [ ].
This Section covers the statements summarized below:
Defines the default physical memory segment to use when the seg-
SEG ment portion of a segment:offset address is omitted.
FILL Stores data directly into physical memory locations.
EXAM Reads data directly from physical memory into program variables.
OUT Sends 8-bit values through physical machine ports.
Invokes a machine code subroutine using a FAR CALL. Machine
registers can be set before the call and retrieved after the call. Your own
CALL machine subroutines can be accessed as a MegaBasic package using
another method described in Chapter 10, Section 5.
Invokes a machine code subroutine using a software INT number
CALL
Machine registers can be set before the call and retrieved after the call.
Defines a MegaBasic procedure that is access externally through a
CALL#
software INTerrupt.
DOS “cmd” Executes an operating system shell command.
Providesaccess to many MegaBasic internal control variables, some
PARAM of which can be altered, all can be read.
Memory access should only be used by qualified programmers and even then avoided
whenever possible. It is very easy to corrupt the machine code of MegaBasic to produce
unpredictable and even disastrous results. Furthermore, programs which rely upon such
techniques will be highly machine dependent and potentially very difficult to move to
other machines or operating systems. These operations are intended for limited use by
systems programmers to perform actions which would otherwise be impossible.
The first form specifies both address components, each of which may be given with a
arithmetic expression, and represents a complete absolute memory location. The second
form specifies only the offset portion of the complete address and the segment portion is
the default segment, defined by an earlier SEG statement (described below). Keep this in
mind whenever using FILL, EXAM or CALL.
Many of the parameters and results of these system interface statements are in strictly
integer form. In MegaBasic you can generally specify numbers in either real or integer
form, but real numbers are converted to integer when the context in which they are
used demands it. Better performance results if you always specify numbers in the same
numeric type as used by the application.
7-44 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
7
Position Register
1 AH
AX
2 AL
3 BH
BX
4 BL
5 CH
CX
6 CL
7 DH
DX
8 DL
9 SI
11 DI
13 BP
15 ES
17 DS
19 CPU Flags
Use BIT(), ASC(), CHR$(), FILL and EXAM to pack/unpack your desired values
to/from the string arguments. CALL is 80x86 CPU dependent and other MegaBasic
versions using different microprocessors may use different but similar conventions. To
send and receive all the registers, the string arguments specified must be the full 20 byte
length (i.e., ten 16-bit registers). Shorter strings access fewer registers, e.g., a length of 5
bytes would access registers AX, BX and CH.
The CPU register string includes the Flags register as bytes 19 and 20 of the register string.
Because of the critical nature of the FLAGS register, you cannot set any of the flag bits
and attempts to so will be ignored (without any reported error). Your program can only
examine the FLAGS in a returned register string. Usually only the CARRY flag will be of
interest and it is returned in BIT(REG$(19),7).
Although there can be good reasons for doing so, passing absolute addresses of
MegaBasic variables to external routines for access and/or modification can be very risky.
This is because MegaBasic variables are moved from time to time to allow efficient
management of the available memory. The very act of using a CALL statement can cause
a shift in memory addresses. The following steps can be used to minimize this difficulty.
h Set the register string to chr$(0)*20, which forces a memory shift if one would have
occurred.
h Setup the register string with your absolute addresses, taking care to use no
user-defined functions or complex expressions that might cause another memory
shift in the process.
h Make the CALL
Interrupts which have been set up by the SERVICE statement (described later) cannot
be accessed by CALL# statements using the same MegaBasic program that set them up.
However, they can be invoked from programs running under other copies of MegaBasic
in the same machine. An Interrupt Service Error results if this rule is violated.
Since the command string is a string expression, you have to surround it with quotes,
but you can also specify it using an arbitrary string expression. Omitting the command
string altogether will exit MegaBasic and return you to the operating system command
shell (however the END statement is preferred).
You specify the operating system commands exactly as if you were typing them at the
operating system command level, for example:
DOS “copy C:*.*B:*” Copies all files from drive C: to drive B:.
DOS “type ”+T$ Types the text of the file named in string variable T$
on the console screen.
Enters the MS-DOS command shell while preserving
the current program state within MegaBasic. At this
DOS “” point you can enter DOS commands for as
long as you want to. Afterward to resume where
you left off, just type the DOS EXIT command to
get back into MegaBasic.
Consult your MS-DOS operating system manual for full information about the available
DOS commands and how to specify them. We describe several important ways to use the
DOS statement below:
h Your programs can request MS-DOS commands from the user with an INPUT
statement for immediate execution by the DOS statement. Hence, your program can
always stay in control without giving up any capabilities.
h If the command string is a null string, MegaBasic invokes the command processor
for command execution from the console, which allows the user to enter as many
MS-DOS commands as desired. To return to MegaBasic, the user must type the
MS-DOS command EXIT, which exits the command processor and returns back to
MegaBasic.
7-46 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
7
h Your program can build a MS-DOS batch file and then execute it with a DOS
command. This is done by simply PRINTing them to an OPEN file whose name has
the extension .BAT (e.g., BATCHFIL.bat). You execute a batch file by giving its name
as an MS-DOS command (e.g., DOS “BATCHFIL”). Upon completion of the last
command in the file, MegaBasic regains control and your program continues on its
way. See your MS-DOS manual for further information about batch files and batch
commands.
h Sometimes you may want to redirect the output of an executing batch file. Given a
file or device name in string variable OUT$ and a batch file name in variable
BATCH$, the following MegaBasic statement will redirect all console messages to the
message destination specified:
Be sure that your batch file ends with an EXIT command to return to MegaBasic.
Other wise, it enters the shell command level and waits for a command while screen
output is redirected away from the screen.
DOS shell commands come in two flavors: internal and external. Internal commands are
those built into the command shell, while external commands are those in separate files, i.e.,
those with .EXE, .COM or .BAT file extensions. External commands always return a
termination or exit code which your program can access from PARAM(19) immediately
after the DOS statement. See the END statement (Chapter 6, Section 1) for more
information on exit codes.
There is one important restriction that you must be aware of in using the DOS statement
to execute DOS system commands: never execute a program which stays resident in
memory after it terminates. An example of this type of program is the PRINT utility
included in the standard set of MS-DOS utilities. Such programs will likely appear to
operate correctly for a while, but later on after MegaBasic regains control and/or
terminates, the system will probably crash or lockup with a memory allocation error at
some point.
You can use most resident programs without any problems by making them resident
before you bring up MegaBasic. For example, you can install the DOS PRINT utility before
you get into MegaBasic, then later invoke PRINT from MegaBasic through a DOS
command. This is necessary as a result of the memory allocation mechanism used within
the DOS operating system; it is not a bug in MegaBasic.
The DOS statement relies on the MS-DOS command processor residing on a disk file
(usually named COMMAND.com), which is temporarily brought into memory to execute
each command. You may notice a slight pause between giving a DOS command and its
actual execution (while loading the command processor). MegaBasic determines the
name of the current command processor by reading it from the set of MS-DOS
environment strings, available to all programs running under MS-DOS.
If MegaBasic cannot find the command processor on the disk under the name specified
in the environment, a File Not Found error will occur. This can happen if you set the
default drive to a drive which does not contain any command processor file. To avoid
this problem, your MS-DOS system should employ a CONFIG.sys configuration file that
contains the command:
SHELL = C:\COMMAND.COM /P
which specifies that the command processor is always found in the root directory on
drive C: no matter what the default drive happens to be. This is especially useful on
fixed-disk systems where you only need one copy of the command processor on the
system. The command processor is usually re-loaded when most programs terminate, so
if the system cannot find it, you have to re-boot from scratch. See your DOS operating
system manual for other information about CONFIG.sys and the SHELL command.
The DOS command is also supported under the Xenix operating system version of
MegaBasic. However, there are some differences that you should read about in
Appendix B.1. MegaBasic does not support it under any of the CP/M operating systems,
nor under the TurboDOS and Convergent Technology operating systems.
7-48 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
7
7-50 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
7
When an interrupt occurs, all interrupts with the same or lower priority become
disabled until the procedure returns. However if an enabled interrupt with a higher
priority occurs, it will be serviced as soon as the next MegaBasic statement finishes
executing. As soon as the higher priority interrupt procedure returns, the lower priority
procedure resumes execution, re-enabling the intervening priority levels. The user must
ensure that in such a case, any common data structures used by the various interrupt
service routines are accessed in a re-entrant manner. The LOCAL statement is useful in
such cases to protect variables that need to be preserved.
Avoid operations that wait for something to occur; after all, the whole idea of logical
interrupts is the elimination of wasteful polling mechanisms. For example, INPUT
statements, INCHR$() function calls or anything else that waits for keyboard input
should not be used inside logical interrupt service routines. INPUT statements that display
prompts and accept edited input are not re-entrant, so logical interrupt service routines
must avoid using them to avoid conflict. This is because interrupting an interactive input
statement to execute another such INPUT statement will leave the original INPUT in an
unpredictable state. The INCHR$() function is, however, fully re-entrant so it can be
used within a service procedure without restriction.
The foreground application should avoid operations of indeterminate duration because
logical interrupts are usually only serviced between MegaBasic statements and excessive
statement execution time will delay interrupt response. Logical interrupts are also
serviced while waiting for single character input (as in interactive INPUT statements and
INCHR$() function calls) and during WAIT statement delays.
It is possible to block logical interrupt servicing by calls to the operating system or to
other resident software that wait or execute indefinitely. This includes shelling-out to the
operating system, direct CALLS to system device input (and sometimes output)
functions, and MegaBasic multi-character inputs (e.g., READing serial devices, INCHR$()
with multi-character requests).
Interrupt service procedures should not be used to process extremely rapid events, due to
the relatively long periods that interrupts cannot be acknowledged (i.e., during
statement execution). For example, one-at-a-time byte transfers at 38.4k baud could be
too fast for this type of system. However events like output buffer empty, input buffer almost
full, machine tool sequence complete, timer expired, concentrator available, mouse moved, hot-key
pressed, pressure threshold reached and message waiting can be handled very efficiently.
Interrupt Control
Variations of the INTERRUPT statement are used to enable and disable logical interrupts
and to select an 80x86 INT number for access by the external processes. Logical interrupt
capabilities provide a complete system for supporting real-time, asynchronous event
processing. The INTERRUPT statement earlier merely defines a logical interrupt
number, it does not enable it. Three other statements are available to enable, disable and
terminate interrupts:
If the interrupt number is omitted from the above statements, all currently defined
interrupt numbers are selected by default and modified accordingly. MegaBasic reports
an error on any attempt to enable or disable an undefined interrupt number. When an
interrupt occurs on a disabled interrupt number, the event is still posted but not acted
on. Later, if the interrupt number is re-enabled, the posted event is serviced
immediately. An interrupt can be redefined by clearing (END) its current definition and
redefining the same interrupt number in an INTERRUPT statement with different
procedure and control settings.
where the <data selector> chooses the value from the available set and the optional
<inter rupt number> specifies the logical interrupt number (0 to 31) for which the data
applies. If the <inter rupt number> is omitted, then the logical interrupt currently being
serviced is assumed. Minus one (–1) is returned if the interrupt is not being serviced, or
if either argument is out of range. The <data selector> argument may take on the
following integer values:
7-52 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
7
The interrupt status value returned for selector 1 contains a number of bit flags that
indicate the current state of the corresponding interrupt. These bits are most easily
accessed using the & operator, e.g., INTERRUPT(l)&4 indicates the status of bit 2.
The overrun flag indicates that the interrupt has been posted more than once before
being serviced by the interrupt procedure. This flag is cleared only when an INTERRUPT
ON or STOP statement is invoked. If overrun is a result of too many events on one logical
interrupt, you can redistribute the events over multiple logical interrupts to lessen the
burden or specify a higher <max post> limit. Overrun can also occur when your
program executes operations of indeterminant duration, such as multi-character READ,
INCHR$ and direct CALLS to similar operations in the operating system and other
resident software, so avoid such actions while logical interrupts are enabled.
Defining it with the INTERRUPT = statement causes that interrupt vector to be linked
into the MegaBasic logical interrupt system. It is this hardware INT that is called by
external software to invoke the MegaBasic logical interrupts. Without defining it, no
execution path to your logical interrupts exists. In multi-tasking systems where more
than one incarnation of MegaBasic is executing, each of the MegaBasic tasks may assign
a different 80x86 INT so that each one independently accesses separate MegaBasic
programs.
Only one 80x86 INT vector is used by the logical interrupt system. If you re-define it as a
different number, the prior INT vector is restored to its earlier contents and the new
vector is set to point into the logical interrupt system. A defined interrupt vector is only
restored when MegaBasic exits or when a program defines another 80x86 INT number.
The pointer in the interrupt vector points to the following structure in the MegaBasic
process segment:
The CALL FAR is a 5 byte instruction that is followed by a word containing the offset in
the interrupt table (segment 0000) of the currently defined interrupt in use. This allows
external processes to determine for themselves if the interrupt vector they are about to
call has been defined. This word will not match the interrupt vector offset if the
interrupt vector was not setup by a MegaBasic INTERRUPT = <INT> statement. The
following assembly code can be used to make this test:
Posting Interrupts
One of the 256 interrupt vectors is reserved for use as the interface that external events
signal to MegaBasic that some event has occurred. This INT number is defined by the
INTERRUPT = <number> statement described earlier. The external process places the
logical interrupt number in AL, sets BX and ES to an optional value to be posted and then
calls the reserved interrupt number. This invokes a short routine within MegaBasic that
posts the event in the internal interrupt control tables maintained by MegaBasic and
then immediately returns.
Except for the carry flag, all CPU registers are preserved by the event interrupt. Invalid
or undefined logical interrupt numbers in AL are ignored. Hardware interrupts are
disabled during this posting operation. The posting call returns with carry set if posting
fails for any reason (e.g., undefined interrupt or exceeding the posting limit), and returns
with carry cleared to indicate a successful post.
It is only when MegaBasic finishes whatever statement it is currently executing, that it
services a posted interrupt (assuming it has a higher priority than any interrupt currently
being serviced). When an interrupt service procedure returns, it will resume program
execution at the point it left off, as long as no other interrupts are pending.
7-54 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
7
This statement enters the shell command level and leaves 5000 bytes of free memory for
any background processing, beyond the memory already in use by the program. The
memory size parameter may be any number of bytes up to the available free space,
minus about 16k for the command shell process. It should generally be limited to the
smallest amount of memory under which the background process can fully execute
without running out of memory.
When a background process gains control during a DOS statement invocation, it
normally retains control until it and all other outstanding logical interrupts pending is
serviced and its associated interrupt procedures have all returned. In some situations,
particularly when the background process is waiting for some event to take place, the
background process may take up too much time before giving the foreground a chance
to execute. Therefore, a special statement can be issued to give the foreground process
immediate control before continuing with the next background statement. This
statement is simply:
INTERRUPT WAIT
After executing this statement, the foreground process resumes until it passes control
back to the background process, which continues with the statement immediately
following the INTERRUPT WAIT statement. In background processes that wait for things
to happen, as in background modem transfers, INTERRUPT WAIT statements should be
invoked in busy waiting loops so that the background process doesn’t monopolize the
CPU unnecessarily while it waits. When no DOS statement is in progress, INTERRUPT
WAIT statements are ignored when executed (i.e., they do nothing).
Background processes must never STOP or END, either directly or indirectly due to an
error, while a DOS statement is in progress. To do so will leave the system with an active
COMMAND.com shell running a foreground process that can neither be removed nor
resumed. In such a state, you can edit and save your program to repair bugs, but the
machine will crash when you exit MegaBasic. Furthermore, all available memory, except
the amount you specified in the DOS statement, will be unavailable for any purpose. The
only viable option at this point will be to re-boot the computer. To minimize the
likelihood that this occurs, the following additional extensions are provided:
h MegaBasic does not recognize Ctrl-C from the console when a DOS statement is in
progress. This is to prevent a Ctrl-C typed into a foreground process from
inadvertently reaching the background process and stopping it.
h MegaBasic does not execute DOS statements while a higher-level DOS statement is still
active and attempts to do so will be ignored. The design of MS-DOS does not support
such an operation and permitting it would immediately crash the system.
h The DOS function can be tested at any time to determine if a DOS statement is
currently in progress. The DOS function returns 1 if a DOS statement is in progress, 0
if no DOS statement is in progress, or –1 if the DOS statement has completed but
logical interrupts serviced before its completion have not yet finished.
All logical interrupts serviced during a DOS statement must be completed (i.e., their
service procedures must return) before the DOS statement is really finished so that the
statement following the DOS statement can be executed. Until this happens, Ctrl-C and
further DOS statements will remain disabled.
7-56 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
7
One of the most powerful aids for controlling complexity of software systems is the
principle of modular design. As programs become longer, they also become more
complex and difficult to work on. To maintain the simplicity of small programs, large
problems can be broken down into a set of smaller component problems which, by
themselves, are easier to solve. If some of these component problems are still too
complex to deal with, they can be further subdivided until the subcomponents become
manageable. Corresponding to each of these sub-components is a program module
designed to solve its problem. This is the technique of modular design, also called divide
and conquer, which is implemented in programming languages using the construct called
subroutines. The concepts and techniques you use to build and use subroutines are
covered in this chapter, as summarized below:
GFK-0256 8-1
8
This chapter discusses the most powerful feature of MegaBasic: subroutine construction
and usage. It describes the collection of features that let you create your own additions to
the language as you see fit, extending its capabilities and tailoring its facilities toward
your own special needs. These features permit previously developed programs to be
used as the building blocks of new, larger programs, which, on completion, become the
primitive components of still larger systems.
To put it simply, a subroutine is nothing more than a section of your program that
performs a specific task, that has been set aside and given a name for you to refer to (and
execute) from anywhere in your program. As you develop programs from subroutine
building blocks, you will gradually accumulate your own useful set of subroutines that
can be used to build new programs with far less effort than earlier ones. In the sections
that follow, we assume that you understand how to build and use programs, and
concentrate on the new concepts about subroutines.
Chapter 10 covers the concept of packages, which lets you collect many of your
subroutines and global data variables into external libraries that can be accessed by your
main program as MegaBasic extensions. Subroutines can also be developed in assembler
(Chapter 10, Section 5) and packages with one or more assembler routines can be
accessed by your program like any other package.
The LIBRARY.PGM file included with the MegaBasic release contains many examples of
useful functions and procedures which illustrate how subroutines can be built,
documented and made accessible for general use by other programs you write.
Meaningful names, line-labels and identifiers are used to clarify their usage and
understanding and they may be freely applied to your own programs without any
further permission from the author. Refer to these routines for more examples of the
concepts described in this section.
8-2 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
8
You can communicate data to procedures and functions through an argument list, which
is used in computing their intended task. Functions always compute a single result
which is used in the expression that invoked the function.
GOSUB <label>
Short for GOTO Subroutine, a GOSUB statement transfers program control to the line
specified (by line number or line-label) as with the GOTO statement. The line transferred
to must be in the same program (package) as the GOSUB statement. When a GOSUB
subroutine has finished its work, the program resumes execution at the statement
following the GOSUB statement. To signal this termination response, a companion
statement called a RETURN must be executed. MegaBasic keeps track of the statement
following the GOSUB so that when a RETURN statement is encountered, control returns
to the right place. There may be any number of RETURN statements within the body of
the GOSUB subroutine, and each of them will resume execution at the same point in the
program.
GOSUBS are best visualized as program blocks that perform a procedure as an
operational unit. Although the body of a GOSUB has no obvious structure required by
8-4 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
8
specify a real result for an integer function (or vice versa), MegaBasic will automatically
convert the expression result to the proper numeric type (i.e., integer-to-real or
real-to-integer). This kind of type mismatch is permitted and no error is reported for its
occurrence, unless for some reason the type conversion could not be completed (e.g., a
real result too large to fit into an integer representation). Nonetheless, you should
always try to provide numbers in the proper representation for the context in which
they are to be used for the most efficient implementation.
Since local variables carry their previous value after the LOCAL declaration, they may be
employed for passing data parameters to GOSUBS. On RETURN, their prior values are
restored, and program execution resumes. Used within recursive procedures (Chapter 8,
Section 5) to create temporary working variables, this is a particularly useful and
powerful tool.
String variables of any length can be localized, but since their previous contents are
saved on the scratchpad until a RETURN is executed, really long strings can easily use up
all the storage set aside for this purpose. Hence LOCAL strings should be used sparingly
and with great care to avoid such problems. The amount of local storage available at any
instant can be obtained from the FREE(2) utility function. The following example
illustrates how LOCAL variables are confined to subroutines:
50 Local X,TEXT$
60 X – –1; Y = X; TEXT$ = “######”
70 Return
When this program is run, the PRINT statement in line 20 will display the line: text string
99999 –1. Notice how the GOSUB was unable to modify both X and TEXT$ (because they
were declared LOCAL) but was able to modify Y because it was not declared LOCAL.
When they are only being employed to prevent interference with variable values,
LOCAL variables are not needed within subroutines which are always called from other
packages (Chapter 10). All variables within external packages are implicitly local to that
package unless explicitly declared SHARED. Such variables are physically different from
those in other packages that happen to have the same name.
Since numeric functions always return either an integer or a real result, you must
somehow indicate the result type of each function. This is may be done explicitly using
the above modifiers in the DEF statement of the function, or implicitly by allowing the
default type to be imposed by omitting the type from the DEF statement. Without the
word STRING, INTEGER or REAL, a function s result type is implied from its name.
However, it helps program readability if all functions explicitly declare their result type.
The rules for assigning data types to names are discussed in Chapter 3, Section 1,
Chapter 4, Section 2 and Chapter 5, Section 1. See the RETURN statement (Chapter 8,
Section 1) for more information about returning the result of a function.
When a function is activated by using it in an expression, an argument list is supplied
which defines values for each of the argument list variables in the order given by the
DEF FUNC statement. Such values may be specified by a general expression, but they
always appear within the function itself as the contents of the argument list variables.
The optional argument list consists of a sequence of unindexed string or numeric
variable names separate by commas and enclosed in parentheses. These variables, called
formal arguments, serve during actual use of the function to hold the data passed to the
function so that they can be processed to form the ultimate result returned. If no
8-6 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
8
argument list is specified in this definition statement, then no arguments can be passed
to the function when it is actually used.
Argument list variables may be either string, integer or real variables, which corresponds
to the argument type to be passed through that argument in the list. You should specify
numeric argument list variables that possess the appropriate integer or real type for the
context in which they are to be used. This avoids unnecessary type conversions which
can degrade the performance of your program.
An argument list has several other optional advanced features which are discussed in
Chapter 8, Section 4. These features include passing data in both directions through the
parameter variables, optional parameters that take on default values when omitted, and
open-ended parameter lists that can have any number of parameters.
If the =<expr ession> at the end of the DEF statement is omitted, then a multiple line
function is defined whose procedural definition must follow. The main body of a
multi-line function consists of a sequence of statements that includes at least one
RETURN <expr n> statement and physically ends with a FUNC END statement (described
below). The following example illustrates a useful multi-line function:
40 Def string func NTH(N); Local P
50 If N mod 100 > 3 and N mod 100 < 21
then Return str$(N)+“th”
60 P = min(N mod 10,4)*2+1; Return
str$(N)+“thstndrdth”(P:2)
70 Func End
FUNC END
Used as the last statement of a multiple-line function to indicate where its DEFinition
ends. Unlike the DEF statement, which must be the first statement on a line, the FUNC
END statement may appear anywhere on a line as long as it is the last statement of the
function. Single-line functions do not use the FUNC END statement, but they are
mandatory in multi-line functions.
within expressions and therefore the parentheses are not needed (and, in fact,
MegaBasic reports an error if they are used). See Chapter 8, Section 4 for important
details concerning the more advanced features of argument lists.
Procedures may have any name that would be legal as a variable name as long as the
name is not used for another purpose elsewhere in your program. You can even assign
procedure names that end with a dollar sign ($) or a percent (%), although such names
are normally reserved for strings and integers. Procedure names do not represent data,
so they do not have a data type as variables and functions do.
The body of a procedure consists of any sequence of program statements which
ultimately must lead to a RETURN statement. More than one RETURN may appear in a
procedure if needed. The very last statement of a procedure must be a PROC END (see
below). Procedures are almost identical with multiline user-defined functions, except
that they are not used in expressions and do not return a result via the RETURN
statement. The following useful procedure illustrates some of these concepts:
100 Rem *** Sort VALUE(L) through VALUE(H) with Quicksort
105 Def shared proc SORT @ VALUE,L,H; Local T,l,J
110 REPEAT; T=VALUE((L+H) div2); I=L; J=H
115 While J>–L and VALUE(J)>T; J =1 ; Next
120 While l<=H and VALUE(I)<T; I += 1; Next
125 If l<=J then
[Swap VALUE(I),VALUE(J); I +– 1; J = 1;
If l<=J then 115]
130 If J–L<H–I then Swap H,J else Swap L,l
135 SORT VALUE,L,H; L=l; H=J; Next if L<H
140 Return; Proc end
This procedure sorts the contents of a range elements in a numeric array into ascending
order. Once defined, its use is as simple as:
SORT ARRAY,FIRST,LAST
Notice how the procedure call appears as if it were a standard MegaBasic statement.
Procedures are specifically designed to encourage the definition of your own new
additions to the working set of MegaBasic facilities. Their similarity to statements is
intentional so that you do not have to remember and apply a separate set of rules to use
them. Read Chapter 8, Section 4 for important additional features of argument lists
which make procedures more versatile.
PROC END
Used as the last statement of a user-defined procedure to indicate where its
DEFinition ends. Unlike the DEF statement, which must be the first statement on a
line, the PROC END statement may appear anywhere on a line as long as it is the last
physical statement of the procedure.
8-8 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
8
Subroutines in MegaBasic are provided as a set of related program constructs which are
very simple and natural to use, yet they provide tremendous generality in their
application. Their effective use, however, requires that you understand the concepts and
motivation behind them. Several types of subroutines are supported under MegaBasic,
but they all involve the following ideas in varying proportions:
Invocation by Name
A subroutine requires some means of identifying it. Hence each subroutine has a name
of some sort. All subroutines are invoked by merely referring to their names. These
names generally refer to the location in the program where the subroutine is defined.
Procedures and functions can even reside in other external packages (described in
Chapter 10).
8-10 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
8
GOSUBSubroutines
The standard method for building program modules in all BASICS is the GOSUB, which
is simply a sequence of program steps which can be invoked from anywhere in the
program, without having to be typed in repeatedly. A GOSUB is universally accepted in
all BASICS with substantially the same meaning and form. The GOSUB itself is any
sequence of program statements that eventually terminates with a RETURN statement. It
is invoked by specifying its beginning line in a GOSUB statement, such as:
GOSUB 1010
which simply says begin execution at line 1010 and come back when a RETURN statement is
encountered. In MegaBasic, you can assign names to individual lines and such lines can
then be referred to by name as well as by line number. If line 1010 were to be named
SORT, then the above GOSUB reference could be stated as follows:
GOSUB SORT
Such names have an immense effect upon the readability of your program and their use
is highly recommended. The naming rules are described in detail on Chapter 2,
Section 5. There is nothing at all special about the program statements performed within
a GOSUB, except that they must eventually execute a RETURN statement. Since it is a fixed
sequence of statements which only perform one specific set of actions, applying a GOSUB
to different situations can be rather cumbersome. Take, for example, the following simple
sort GOSUB:
1000 Rem *** Sorts the N values of array TABLE
1010 SORT: For l=1 to N–1; For J=1+1 to N
1020 If TABLE(I)>TABLE(J) then Swap
TABLE(I),TABLE(J)
1030 Next J; Next l; Return
To apply GOSUB SORT to any set of numbers involves loading the numbers you wish to
sort into array TABLE(), setting N to how many numbers it contains, then invoking the
subroutine with the statement: GOSUB SORT (or GOSUB 20). It is easy to see that in some
situations, just setting up the variables in preparation for using a GOSUB might well
require more effort than what the GOSUB itself performs. GOSUBS are useful in simple
applications, but MegaBasic provides a much more powerful and flexible construct,
called a procedure, which you should use when new programs are developed.
MegaBasic supports GOSUBS primarily to support existing programs that use them.
Procedure Subroutines
Procedures differ from GOSUBS in three ways. First, their definition begins with a DEF
statement that gives the procedure a name and creates a set of channels through which
information is communicated to and from the procedure, called arguments. Second,
procedures are invoked by stating their name followed by any required input data (no
GOSUB prefix is used). Procedure calls therefore appear quite similar to built-in
MegaBasic statements. Conversely, new statements may be added to the built-in set by
defining them as MegaBasic procedures. Third, your programs can refer to procedures
defined in other programs if necessary, which further enhances the view that procedures
are language extensions. This particular topic will be covered later on in Chapter 10. The
preceding sort GOSUB has been rewritten as a procedure for the following example:
1010 Rem *** Sorts the N values of array TABLE
1015 Def proc SORT @TABLE,N
1020 For l=1 to N–1; For J=1+1 to N
1030 If TABLE(I)>TABLE(J) then Swap
TABLE(I),TABLE(J)
1040 Next J; Next l; Return
1050 Proc End
The only difference with the GOSUB is that line 1015 has been added to define the
procedure name and its input data. The input data is communicated to the procedure as
a list of arguments, a subject to be covered in the next chapter. SORT as defined by this
procedure can be invoked with different sets of input data without any additional
changes, as follows:
SORT NTBL,200
SORT KEYS,LENGTH-UNUSED
SORT VECTOR,M*N
where NTABLE, KEYS and VECTOR are all one-dimensional arrays containing the data
to be sorted. Communicating data via this mechanism provides tremendous flexibility
for applying procedures to varying situations. Procedures are therefore recommended
over GOSUBS in all significant applications. A good rule of thumb is to use GOSUBS only
for very small subroutines which are called from nearby lines of a single larger routine.
GOSUBS in existing programs can be easily converted to procedures which have no
arguments to begin with, and later enhanced with argument lists as needed. (You can
also leave in all GOSUBS if you don’t want to bother converting them into procedures.)
8-12 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
8
The <optional argument list> is a list of variables, separated with commas, through which
input data and output results may be communicated. This construct permits the
procedure to deal with one set of variables that represent any data being communicated
with its user. This important topic is thoroughly treated Chapter 8, Section 4. Procedure
argument lists of both their definition and their references are never surrounded by
parentheses as they are in functions, which gives procedure calls the appearance of
MegaBasic statements.
The procedure body of statements is exactly like the body of a GOSUB designed for the
same task. It must have at least one RETURN statement so that it can continue execution
in its calling routine when finished. The very last statement of a procedure definition
must be PROC END, which defines the end of the procedure definition.
Function Subroutines
Functions differ from procedures in two ways. First, functions are invoked from within
string or numeric expressions, rather than as statements in themselves. Second,
functions must return a single result value (string or numeric) back to their invoking
expressions, as part of their final termination. This result is returned by specifying it in
the terminating RETURN statement (Chapter 8, Section 1). Functions are identical with
procedures in all other respects.
By defining your own functions, frequently used computations can be programmed
once, and later referred to by name as often as necessary anywhere else in your
program. This centralizes its internal implementation details in one place in the
program, so that if the computation is modified in the future, all places that use it are
automatically updated. Furthermore, properly designed functions can be independently
used without knowledge or consideration of their internal workings, freeing you to
solve the problems at hand instead of being side-tracked by lower level details.
As with built-in functions, user-defined functions are named and include an argument
list. Any name legal for assignment to a variable is also legal as a function name. As with
variables, function names must reflect the data type that is returned. A dollar sign ($)
ending the name indicates that the function generates a string result; functions named
without a dollar sign must generate a numeric result. Unlike most BASICS, function
names do not have to begin with the letters FN. Once assigned as a function, a particular
name cannot be used for any other purpose. Name formation in MegaBasic is covered in
detail on Chapter 1, Section 5.
The argument list of a function must be enclosed in parentheses and may contain string
or numeric argument expressions. Both the number of arguments and their data type
must correspond to your definition of the function (described below). The argument list
doesn’t appear if no arguments are required for its operation. Argument lists are
discussed in Chapter 8, Section 4.
MegaBasic supports two different forms of function definition. The simplest and most
common form is called a single-line function. Functions of this type merely define an
expression (string or numeric) which is evaluated when the function is invoked. Such
functions are then applied as shorthand for the expression whenever required.
For more complex applications, multi-line functions can be defined. These are not
restricted to just one line or one fixed result expression. Multi-line functions may contain
as many statements as necessary to compute the desired result. Because no limit is
imposed on their size or content, such functions may perform many complex
computations, alter global data structures, or perform input and output transfers prior to
returning their ultimate result.
The <name> specifies the unique name by which the function is referred to throughout
the program. The optional <ar gument list> lists in parentheses the variables through
which the argument data is passed to the function. The <expr ession> specifies a string or
numeric expression combining the arguments (with possibly other data) into a new
result which is passed back to expression invoking the function.
An optional <type> may be specified on numeric functions to control the result type of
the function. You may specify one of the words STRING, INTEGER, or REAL for this
option. If you omit the <type> option, the function result type is derived from the
function name itself. Names ending with a percent sign (%) are integer; names ending
with an exclamation mark (!) are real; and names ending with a dollar sign ($) are string.
Other wise the function result type depends on earlier DEF statements (Chapter 5,
Section 1) which may have assigned a numeric or string type to the leading letter of the
function name. In the absence of any type declaration, a function will return a real result
by default. We recommend that all function definitions explicitly include its full type
declaration as a matter of programming style and general readability.
The result <type> merely ensures that the result is always of the desired type specified,
regardless of the actual numeric computations performed within the function and the
result expression. MegaBasic will automatically convert the numeric result of a function
to its defined type whenever the type of the RETURNed result differs. A Data Type Error
wi1l be reported if you attempt to return a string result from a numeric function or return a
numeric result from a string function.
Single-line functions provide a simple way to combine data using a complex expression,
for example the definition:
The variables N and M are function arguments which will be used to represent the data
presented by an actual reference to the function, such as in:
MODULO(X–1 7,SQRT(Y))
When this reference to MODULO is made, the formal arguments, N and M, are set to the
values expressed by X–17 and SQRT(Y) respectively. N and M are then used within the
expression given in the DEFinition of MODULO, i.e., N–INT(N/M)*M. This expression
is evaluated and the result is passed back as the value of MODULO, which may then be
invoked within a higher level expression, as in:
This is a complete assignment statement which sets X to the logarithm of the sum of two
different references to MODULO in the same expression. Numeric argument definition
variables, like N and M above, have no relation to variables of the same name used
outside the function definition, because they exist only during the active execution of the
function. The argument list has some important properties and options which you
should read about in Chapter 8, Section 4.
8-14 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
8
The only difference between single and multiple line function DEF statements is the
absence of the equal sign (=) and <expr ession>. Instead, the main body of the function
immediately follows the DEF statement. This main body consists of whatever series of
program statements are necessary to perform the desired task and return the result
(except that it cannot include another DEF statement).
To pass the result back to the expression that invoked the function, a multiline function
executes a RETURN statement specifying an expression that computes the desired result.
Any number of RETURN statements may appear within a multiple line function (just like
a procedure), and when any one of them is executed, its result expression is computed
and passed back to whatever expression invoked the function, causing the program to
resume from that point.
The <r esult exprn> must compute a numeric result for numeric functions and a string
result for string functions. As described for single-line functions, an optional <type> can
be specified to define a numeric function result as STRING, INTEGER or REAL. You
should understand the material presented in Chapter 3 in order to make the best choice
of real and integer numeric types.
This simple but useful function logically tests a string for whether or not it correctly
represents a numeric constant. It returns 0 (false) if an error occurs when VAL(N$)
attempts to convert N$ to a number, or returns 1 (true) if successful. Hence
VALSTR(“234O17”) returns 1 and VALSTR(“$7,235.98”) returns 0. VALSTR() is defined
as an integer function because its logical result is returned faster.
Multiple line functions must physically terminate with a FUNC END statement. You
cannot define other functions within function definitions, but you can define them in
terms of other functions by employing user-defined functions as components to
compute higher level results.
The first side-effect is the problem of global variables changed within the subroutine and
is one of the most frequently encountered sources of programming errors when
programming in BASIC with subroutines. In the example below, variables outside a
function subroutine are affected by the function call, causing the FOR..NEXT loop in line
200 to continue forever:
The problem in this example is that the same loop index variable (I) is used by two
different but nested loops, a condition which is easily hidden by the function itself. You
must ensure that this kind of situation never happens in your programs by restricting
potentially harmful variable accesses. Two methods are available for controlling variable
access:
h Every time you use a variable, find out how and where it is used elsewhere in
the program and make appropriate changes as needed, and
h Define temporary variables used in subroutines as LOCAL variables with the
LOCAL statement.
Data stored in variables must be preserved for the term of its usefulness. Variables which
contain long-term data must be protected from unintentional use, especially in large
programs. You may safely obtain temporary storage by using available local function
argument variables (if any), or by temporarily creating new variables with the LOCAL
statement (Chapter 8, Section 1). Both methods can be employed, but using LOCAL
variables is preferred.
The second type of side-effect is rather obscure but you should be aware of it. READ or
WRITE statements containing user function calls which in turn perform their own READS
or WRITES to the same file, can upset the current file position causing the original READ
or WRITE to access the wrong file position. For example if you directly WRITE the result
of a function that itself accesses the same file, the data will be written at the file position
left by the function call rather than the position specified by the original WRITE
statement. Or suppose that a READ statement includes a user function that CLOSES the
file in its procedure. Such an operation would produce highly unpredictable results.
Awareness of this side-effect is essential to prevent it from occurring. There are two
ways to avoid this problem. At the call level, you can always store the function result in a
variable for subsequent use in the READ or WRITE statements. At the function level, you
could save the file positions of all files accessed by the function and restore them just
before returning back to the caller. This is an excellent solution because it hides the
details within the function and the caller does not have to know anything about it.
8-16 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
8
Useful subroutines take some input data, do something with it, and produce some effect
or result. Inputs must be accessible to the subroutine and the set of output results
produced need to be posted in some way useful to its caller. Except for the RETURN
<expr ession> of function subroutines, all data communication between subroutines and
their application context is through variables. The various techniques to do this are
presented below.
Global Variables
In MegaBasic, like all BASICS, all variables may be accessed throughout the program.
This type of access is called global access and such variables are referred to as global
variables. Variables that contain the results of one statement are therefore accessible to
any other statement that chooses to use them. The output of one subroutine becomes
the input of another. This kind of data communication has no limitations other than the
ability of the programmer to manage the relationships of the variables involved.
However, as more variables come into play, the task of managing variables can become
more difficult. Each use of a variable must be checked out by a thorough examination of
its uses throughout the program. The searching capability of the LIST command
(Chapter 2, Section 2) and the NAMES command (Chapter 2, Section 3) is useful for this.
The XREF command (Chapter 2, Section 5) generates a cross reference listing of the
various identifiers showing where they are referenced. Each global variable should be
documented and its purpose restricted to a single use.
Meaningful names assigned to variables, lines, functions and procedures are an
important means of managing your program. Depending on the application, a naming
standard can be useful to indicate the purpose of each variable by characteristics
embedded in its name. For example, by naming truly global variables with names of six
or more characters and restricting the names of variables used for temporary storage to
five or less characters, it will be more difficult to accidentally use a global variable for
temporary purposes. In the long run, such self-imposed standards can save you a
significant amount of debugging time.
The problems associated with global variables are an unavoidable result of the
unrestricted access to variables, a standard feature of BASIC itself. Without restrictions
on global variable access, programs larger than 40 or 50k bytes become difficult to
develop and maintain. Fortunately, MegaBasic has some automatic mechanisms to
handle these sorts of problems: argument lists and their related local variables.
Argument Lists
As seen above, communicating data through variables has some rather severe
shortcomings. Instead of having to explicitly assign values to the variables required by
the subroutine, MegaBasic supports a mechanism for automatically assigning them,
called an argument list. When invoking a subroutine that uses an argument list, the data
you wish to communicate is listed after the name of the subroutine without necessarily
being stored in any particular variables. When the subroutine begins execution, this
input data becomes available as a list of variables corresponding to each input argument.
The following example illustrates how this works. Suppose that you need to compute
the nearest multiple of one number to another. This simple function would be defined
something like this:
Def func NEARMULT(V,MULT) = round(VlMULT)*MULT
The argument list of this function is defined with two numeric variables, V and MULT,
which are called the formal arguments of the function. The actual work of the function is
performed by the expression to the right of the equals sign (=), which uses the two
arguments in a computation that determines the result desired. When invoked,
NEARMULT uses any pair of actual arguments that you wish to submit to it for calculation
and they do not even have to be stored in variables, for example:
NEARMULT(XIY,Z–2)
MegaBasic sets V to the result of quotient X/Y, sets MULT to the difference Z–2, and then
begins function execution. These two argument expressions are called the actual
arguments of the function. Even though NEARMULT only knows how to operate with two
fixed variables (i.e., V and MULT), it can accept any arbitrary pair of input numbers.
Subroutine arguments can be strings as well as numbers and when strings appear as
actual arguments, their corresponding formal arguments in the argument list definition
must be string variables. In other words, the data types of the actual and formal
arguments must agree. MegaBasic assumes that the definition is correct when they don’t
agree, and reports an error in the reference to the subroutine. For example, if you supply
a string expression as one of the arguments to the function above, MegaBasic reports a
Data Type Error.
You can independently specify formal argument variables with several optional
capabilities. Different argument passing modes are supported to let you pass actual
arguments as values, as variables or as pointers. This is covered in the next few pages.
The number of arguments specified in subroutine references normally matches the
number defined for the subroutine. However, you can also specify optional arguments
along with default values that are passed in the event that the corresponding actual
argument is omitted. This powerful feature is described later on in this chapter.
8-18 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
8
Value arguments are common in most applications because of their generality and
simplicity. The other argument modes have very important uses and can simplify and
speed up certain types of processing. The concepts and motivation for each mode will
now be discussed in full detail.
Value Arguments
Formal arguments defined in subroutines are nothing more than simple variables, which
may actually have other uses in the program (outside the subroutine). When you pass
data to a procedure or function through the variables listed in the subroutine definition,
the following steps are performed internally:
h All of the actual data values to be passed are completely evaluated, as each
argument may be expressed by a general expression.
h The current contents of the variables listed in the argument list definition are
saved so that upon RETURN, they can be restored to their original values as if
nothing happened.
h The previously evaluated actual data values to be passed are then copied into the
corresponding argument variables for subsequent use within the function.
Numeric arguments are converted to the same type as the argument variables
receiving them.
MegaBasic preserves the contents of all formal argument variables during the entire
execution life of a subroutine. In other words, the contents of each formal argument
variable defined by a subroutine is the same before and after invoking that subroutine,
regardless of what information is passed through it. Once a subroutine RETURNS, its
formal argument variable values disappear and are forever lost. Because they effectively
exist only within the context of subroutine execution, such variables are called local
variables. The following procedure illustrates an example of this:
10 LIMIT = 999999,; Print LIMIT; COUNT_TO 10;
Print LIMIT; END
20 Def proc COUNT_TO LIMIT
30 For LIMIT = 1 to LIMIT; Print LIMIT,; Next
40 Return; Proc end
When RUN, this program produces the following output:
99999912 3 4 5 6 7 8 910 999999
Although LIMIT is altered within procedure COUNT_TO, its value on the outside of the
procedure is unaffected. The LIMIT variable inside the procedure is, for all intents and
purposes, totally separate and distinct from its namesake outside of the procedure.
Other variables used within subroutines that are not formal argument variables will be
treated as global variables, and their values persist after the subroutine terminates.
The fact that you can always temporarily store information in local variables without
ever worrying about overwriting global information is an extremely useful and
simplifying mechanism. It means that you can develop subroutines which can be used in
any context without fear that they may alter surrounding variables in unplanned ways.
Local variables are so important that MegaBasic includes a LOCAL statement
(Chapter 8, Section 1) which effectively creates local variables for whatever temporary
(local) application you desire.
The following program uses a LOCAL statement to implement the previous example
using a GOSUB instead of a procedure:
10 COUNT = 999999; Print COUNT,;
GOSUB 20; Print COUNT;
End
20 Local COUNT;
For COUNT = 1 to 10; Print COUNT,;
Next; Return
When RUN, this program produces the following output:
9999991 2 3 4 5 6 7 8 910 999999
Even though the COUNT variable is modified within the GOSUB, it remains unchanged
from the view of the routine which called the GOSUB. Use local variables to isolate the
inner workings of your subroutines, confining their effects on the outside world to only
well-defined and documented sets of intended output result variables.
String variables may be passed by value as localized variables but remember that their
prior string value is saved in internal scratchpad memory until the RETURN statement is
executed. This can quickly use up the 55k bytes (approx.) that they possesses if used
indiscriminately. Furthermore, the formal variable argument itself must be of sufficient
capacity to contain the actual input argument string. A Length Error results if the input
argument exceeds the size of the formal variable.
8-20 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
8
String variables used solely for local purposes should be assigned an initial value of a
null string ( ) during program start-up (initialization). This will prevent the unnecessary
saving of a full variable of spaces ( ) onto the scratchpad stack when the string is
declared local or used as a local parameter in an argument list.
8-22 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
8
required to localize that variable may be undesirable. Copied arguments are not localized
and therefore execute faster and require no internal scratchpad storage. Secondly,
variables in external packages (see Chapter 10) are local unless explicitly declared as
SHARED. Such variables, if not used for any other purposes within the external package
itself, can be used as copied arguments while retaining the properties of local variables.
This function computes the logarithm of V to any BASE specified or to base 10 if not
specified. Notice the BASE=10 expression in the argument list. This syntax specifies the
default value to use for parameter BASE if no second argument is specified. Any
argument defined in this manner may be subsequently omitted from the argument list
when calling the subroutine. For example LOGARITHM(2) returns Log 2 base 10,
LOGARITHM(3,12) returns log 3 base 12. With this general idea in mind, the additional
rules for defining and using default parameters are listed below:
h Parameters can only be omitted from right to left, i.e., you cannot omit
parameters out of the middle, unless you also omit all parameters to the right of
them. Be sure to define default values for every parameter allowed to be
omitted.
h When all parameters are omitted from function references, you must also omit
the parentheses that surround the argument list (i.e., don’t leave an empty
parenthesis shell).
h Default values, when specified, immediately follow the formal parameter
variables in the subroutine argument list definition with an equals sign (=)
separator. A default value may only be a constant or a simple variable (no
subscripts or indexing) and must match the data type of its corresponding
parameter variable. General expressions as default values are thus not
supported.
h When a simple variable is the specified default value, its effective value is its
contents prior to the evaluation of any earlier parameters. This is because
MegaBasic evaluates the entire calling argument list before binding the values to
the formal parameter variable list in the definition.
h Default values may also be assigned to parameter variables that are passed as
variables (preceded by @) or passed by copying (preceded by %). The default
value of an at-sign (@) variable must be a simple variable, never a constant.
Default values for percent sign variables (%) are identical to default values for
regular (local) parameters.
Generally, it is convenient to use default values that can be identified by their values as
default values. For example, a null string ( ) is a useful default value for a string
parameter that would never be a null string. When the subroutine tests this parameter
and discovers that it is null, a different course of action for the omitted parameter can be
taken.
Because optional parameters may only be omitted from right to left, you should
carefully design your argument list definition so that the optional parameters are
ordered from most often required to least often required (left to right). In this way, the
references to these subroutines will tend to use the minimum number of calling
arguments throughout your program.
Default parameters are passed to the subroutine more quickly than specified arguments.
However you should remember that no matter how many arguments you actually
supply when calling your subroutine, MegaBasic always evaluates all of the arguments
in the defined formal argument list. Therefore unnecessary optional parameters that are
never needed in practice should be avoided for best performance. Also, the addition of
default values to subroutines definitions does not in any way slow down subroutine calls
that include all the arguments.
Default values that change with program conditions are implemented by specifying the
default values with simple variables. For example, suppose that whenever you omit a
8-24 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
8
certain parameter you want it to default to the last value actually specified for it. If this
parameter is the variable LAST, you might implement this type of default using the
parameter definition: %LAST=LAST. The percent causes LAST to retain its value after
the subroutine terminates. Hence if you omit this parameter, it simply takes on its
current value, i.e., the value it had upon return from the subroutine (assuming nothing
else altered it).
One of the really important applications of optional parameters is the extension of
existing MegaBasic packages in an upward compatible manner. Suppose that you have a
package that is used by many people, for instance, a data base function library. As time
goes by, you discover that some of the functions it contains can be modified to cover a
wider range of applications, but you are prevented from realizing these expanded
capabilities because a change in their argument structure would make the package
incompatible with existing programs using it. You can probably see by now that
appropriately defined optional parameters are the key to expanding such functions. All
existing programs using the package can execute as before, while at the same time new
or upgraded software can take advantage of the extended capabilities they provide by
using the additional parameters.
The ARGUMENT statement only works directly inside the subroutine and cannot be used
from within other lower-level subroutines. In other words, you cannot have open-ended
arguments read by calling other subroutines.
An open-ended argument list is, by definition, a list of some unspecified number of value
expressions, separated with commas. Your program needs some way to determine when
the list has been exhausted, so that it can stop reading arguments. You could use ERRSET
to trap the error that results from trying to read past the last argument with the
ARGUMENT statement. However the ARGUMENT statement can be used as a function (no
arguments) that returns True (1) if there is one or more unread arguments remaining on
the calling argument list, and False (0) if no more arguments. Line 120 in the example
tests the ARGUMENT function in the WHILE condition to determine the presence of
additional unread arguments and if true (1), executes the loop to evaluate the next one
and process it.
MegaBasic permits your function or procedure to return without reading all the
arguments that it was called with. Such unread arguments are never evaluated, so no
computational effort is wasted on them. A good example of this is the AMONG()
function, which returns the position of a number in a list of numbers, or zero if it
matches none of them:
100 Rem *** Return the position (1,2,..,n) that X match a value
in a list
110 Def integer func AMONG(...); Local X
120 Argument X; I = 1
130 While argument; argument Y;
If X–Ythen return l; 1+~1; Next
140 Return 0; func end
This function returns a result as soon as the first argument is found to equal any one of
the other arguments, or when the list is exhausted. For example, AMONG(323, 567,12, 87,
323, 999) = 4, AMONG(–34, 8256,7) = 0, etc.
Up to now, we have examined open-ended argument lists of numbers for functions. In
the example below, we have defined a procedure that prints each of the arguments
listed, regardless of their type (string or numeric). Notice that error trapping is needed to
do this because a Type Error will occur if a string (numeric) argument is read into a
numeric (string) variable. By trapping the error, the argument can be re-read using a
different variable, allowing the process to continue.
100 Rem *** Procedure that displays any sequence of arguments
110 Def proc PRINT_LIST...; local V, V$
120 While argument
130 Errset 140; Argument V; Print V; Goto Next
140 Errset 150; Argument V$; Print V$; Goto next
150 Errset #99, “Bad Arguments”
160 Next; Return; Proc end
8-26 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
8
You are free to employ the subroutine you are defining within its own definition. Known
as recursion, such subroutines must ultimately reduce down to a result without
reference to themselves in order to terminate in a finite amount of time. Otherwise they
continue to invoke themselves until all the subroutine control space in the machine is
consumed, ending in a Scratchpad Full Error. Recursive subroutines often split a problem
into several smaller but similar problems, then call themselves to solve each of these.
A simple application of recursive programming is the process of determining the
greatest common denominator of two integers (GCD for short). It can be shown
mathematically that GCD(n,m) = GCD(m mod n, n), where 0<n<m. This equation
defines GCD in terms of itself, making it a recursive definition. The following program
shows how it is implemented in MegaBasic:
10 Rem –– Recursive Function for the
20 Rem –– Greatest Common Denominator
30 Def func GCD(VAL1 ,VAL2)
40 If VAL1 > VAL2 then Swap VAL1 ,VAL2
50 If VAL1 then Return GCD(VAL2 mod VAL1 ,VAL1)
60 Return VAL2; Func End
Line 30 names the function and defines its arguments. Line 40 ensures that VAL1 is not
greater than VAL2, because we will be taking the remainder of VAL2/VAL1 using the
MOD operator. This is justified because the GCD of two numbers is the same regardless of
their order. Line 50 tests VAL1 to see if it is non-zero and if so, returns the result in terms
of another GCD evaluation, i.e., it calls itself. If VAL1=0 then the GCD must be the value of
VAL2, i.e., the GCD of zero and any other value is that value.
One of the important aspects of recursive programming illustrated above, is the essential
property that the problem is ultimately reduced down to a result which is NOT
recursively defined. The GCD function repeatedly reduces the original numbers supplied
into smaller numbers which have the same GCD. When one of these numbers eventually
becomes zero, the final result is known and does not required further recursive
processing.
The above example also illustrates how hard it is to guarantee the a recursive subroutine
will eventually terminate in all cases. Upon careful examination of it, you may notice
that if VAL1 or VAL2 is negative (less than zero), then GCD will never reach a point of
termination and ultimately uses up all remaining scratchpad space and terminates the
program by force (i.e., by an error).
Many recursive programs can actually be reformulated as iterative procedures (i.e.,
implemented with loops) rather than recursively. Iterative implementation usually
executes a little faster and consumes less memory. This being the case, why do we bother
with recursive programming? The answer is that certain problems are more naturally
defined recursively and their solution can be much more obvious and simply specified in
a recursive manner.
The key to recursive programming is in recognizing that certain problems can be
naturally defined in terms of themselves and to avoid recursive solutions to those
problems that are not. As with all tools, selecting the one most suited to the problem at
hand leads to the quickest solution. Recursive programming is a surgeons scalpel, not to be
confused with a construction pile-driver.
Another important consideration in constructing recursive subroutines is knowing when
to use local variables and when to use static variables. Static variables are those that keep
their contents after the subroutine returns, and local variables are those whose contents
live only as long as the current subroutine invocation.
Variables used to store temporary or intermediate resulting within the recursive
subroutine should usually be local variables. This is because you do not want a recursive
re-entry into the same subroutine to overwrite the contents of such variables before they
are used. Temporary variables having no further use before a recursive re-entry do not
have to be local. Static variables can be used to store data that is never modified (i.e.,
read-only), or for input/output data that the subroutine modifies along the way.
A subroutine does not have to call itself directly to be recursive, it is also recursive if it
invokes a different subroutine that, at some point, invokes the original subroutine again.
This is known as indirect recursion and is not really any different from direct recursion
except that it is less obvious. It sometime arises accidentally in larger programs when the
programmer calls a subroutine that eventually get back to the same point in the program
before returning. This can be a difficult situation to diagnose and the TRACE RET
command (Chapter 2, Section 4) can be useful for this.
8-28 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
Chapter 9 MegaBasic Built-in Function Library
9 section level 1 1
figure bi level 1
table_big level 1
This section provides a complete description of all the built-in functions in MegaBasic.
These should be studied for utility in your particular applications since there may be
several that already do what you have in mind and they run many times (even
hundreds of times) faster than similar procedures implemented in more primitive BASIC
statements. This function set has been carefully selected to cover the widest variety of
applications with a minimal number of separate function entities (when applied in
combination). Chapter 3, Section 7 covers how to apply most of these functions to entire
vectors of numbers (instead of just scalars). For easy referral, the built-in functions have
been grouped into the following subsections:
GFK-0256 9-1
9
9-2 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
9
LEN(<string expression>)
LlNE(<device number> )
LN(<numeric exprn> )
LOG(<numeric exprn> )
MATCH(<sear ch string>,<match string>,<starting location>,<count>)
MAX$(<stringl>, <string2>,... <stringN>)
MAX(<list of numeric expressions>)
MlN$(<string1>, <string2>,... <stringN>)
MlN(<list of numeric expressions>)
MOD(<numeric exprn>,<modulus exprn> )
OPEN$(<file number>)
ORD(<string> [<bit range>l)
OUTPUT(<device number>)
PARAM(<parameter number>)
Pl
POLY(<real value>, <real array name>, <polyn degree> )
POS(< device number> )
REAL(<numeric exprn> )
RESEQ$(<tar get string>,< stepsize> )
REV$(<string expression>)
RND(<numeric exprn> )
ROTAT$(<string exprn>,<r otation distance>)
ROUND(<numeric exprn> [,<significant digits>] )
SEG(<variable name>)
SGN( [<sign exprn>~ <value exprn> )
SlN(<numeric exprn> )
SPACE(<drive number>)
SQRT(<numeric exprn> )
STR$( <numeric expression> [,<format string>] )
STRUCT(<field> [,<selector>l)
SUBDlR$(<prior subdirectory name string>)
SUM(<vector expression>)
TAN(<numeric exprn> )
TIME$
TRAN$( <string>,<original chars>,<translated chars>)
TRAN$(<string>:<translation map vbl$>)
TRlM$(<string expression>)
TRUNC(<numeric exprn> )
VAL(<string expression>)
[<variable name>]
The functions described in this section perform simple arithmetic operations on numbers
such as rounding, truncating, absolute values, remaindering, random values, sign
transfer, etc. Following the summary below, each function will be described in detail.
9-4 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
9
One common use of CEIL() is finding the smallest multiple of a number that is not less
than another. The expression CEIL(X/Y)*Y returns such a multiple of Y with respect
to X. This expression is useful enough that it can be expressed in MegaBasic as x CEIL
Y, and computed much more efficiently (especially with integer operands). Using this
syntax, CEIL is being used as an operator (like + and –), rather than as a function.
TRUNC(<numeric exprn>)
Returns numeric expression value with any fractional part removed. For negative
numbers this is equivalent to the CEIL() function and for positive numbers this is
equivalent to the INT() function (described above). For example: TRUNC(3.4) = 3/
TRUNC(–2.71) = –2, TRUNC(7) = 7.
This function is only useful with real numbers, since TRUNC() of an integer is the same
value. TRUNC() always returns a result of the same numeric type (integer or real) as the
argument supplied. Like the CEIL() and INT() functions, the expression
TRUNC(X/Y)*Y can be expressed as x TRUNC Y, which is computed much faster with
integer operands.
MOD() accepts either real arguments or integer arguments. If they are not both of the
same numeric type then one argument is converted into the type of the other before
calculating the modulo. MOD() returns a result in the same numeric type as the type of
its arguments (after making them the same). MOD() may also be used as an operator:
X MOD Y =MOD(X,Y). MOD() executes faster with integer operands than with real
operands.
MOD() has zero precision loss for all arguments in both integer and floating point modes.
This is important because a major use of MOD() is in range reduction applications.
MOD(X,Y) requires time proportional to the magnitude difference between X and Y, and
it is extremely fast for magnitude differences under 1010 because no multiplies and
divides are used. It is somewhat slower when X and Y have an extremely large
magnitude difference (e.g., 1050 MOD 10– 50) because it uses the method of successive
scaled subtraction. Although MOD() introduces no error into the result, a meaningful
result requires absolute accuracy in the original X and Y values, especially for large
magnitude differences.
FRAC(<numeric exprn>)
Returns the difference between the number specified and the next lower integer, i.e., the
fractional portion of a number. This function is only useful with real numbers, since
FRAC() of an integer is always zero. FRAC() always returns a result of the same
numeric type (integer or real) as the argument supplied. For example: FRAC(3) = 0,
FRAC(4.23) = .23, FRAC(–7.2) = .8, FRAC(3.4) = .4.
ABS(<numeric exprn> )
Returns the positive (absolute) value of the numeric expression specified. For example:
A85(–25.3) = 25.3, ABS(17.1) = 17.1, ABS(0) – 0. ABS() always returns a
result of the same numeric type (integer or real) as the argument supplied.
SGN(<numeric exprn> )
Evaluates the numeric expression and returns –1 if it is negative, 0 if it is zero, and+l if it
is positive. For example: SGN(–4.5).=l,SGN(o) = o,SGN(352l)=l. This result
always has an integer type, regardless of the numeric type of the argument.
9-6 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
9
RND(<numeric exprn> )
Returns a pseudo-random number sequence uniformly distributed over the interval
0...1, not inclusive. The value of the argument expression controls the method of
computation:
The argument to RND() is used only as a real value, hence an integer argument will be
converted to real before any computation begins. The result produced by RND() is, of
course, a real value. RND without arguments is equivalent to RND(0).
9-8 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
9
The functions described below all use real argument values to produce real result values.
When integer arguments are supplied to them, MegaBasic automatically converts them
to real form. Most of these functions are the so-called transcendental functions, which
include trigonometric functions, logarithmic and exponential functions and square-root.
Such functions return, out of necessity, approximations to the desired value rather than
exact answers. MegaBasic computes these functions with accuracy out to the last digit of
the prevailing floating point precision in most cases. For a small percentage of cases, the
least significant digit will be off.
SQRT(<numeric exprn> )
Returns the square-root of the expression value specified. An out of bounds error will
occur if a negative argument is supplied to this function. For example SQRT(9) = 3,
SQRT(1) = 1, SQRT(5.7) = 2.38746727, etc.
LOG(<numeric exprn> )
Returns the logarithm, base 10, of the expression value specified. An out of bounds error
will occur if a negative or zero argument is supplied to this function. For example:
LOG(1000) = 3, LOG(l) = 0, LOG(.123) = –.9100948886, etc.
LN(<numeric exprn>)
Returns the logarithm, base e (2.7182818...), of the expression value specified. An out of
bounds error will occur if the argument specified evaluates to a negative or zero value.
EXP(<numeric exprn> )
Returns the constant e (2.7182818...) raised to the power specified by the numeric
expression argument. An out of bounds error will occur if this argument is above
+145.06286 (or +709.72683689 in IEEE), because it produces a result too large to
represent in MegaBasic floating point representation. If the exponent is below
–147.36549 (or –708.39641846867 in IEEE) then the result underflows and a zero result
is returned without any reported error.
PI
Returns the constant pi (3.141592..) rounded to the prevailing precision of MegaBasic.
SlN(<numeric exprn>)
Returns the sine of the angle specified by the numeric expression. This angle must be
expressed in units of radians, not degrees. To obtain the sine of an angle expressed in
degrees, you must multiply that angle by the constant pi/180 before taking the sine.
ASlN(<numeric exprn> )
Returns the angle in radians corresponding to the sine specified by the numeric
expression argument. An out of bounds error will occur if the argument is less than –1
or greater than +1. The result returned is always an angle in radians between –pi/2 and
+pi/2. To convert this radian result into degrees, you must multiply it by the constant
180/pi.
COS(<numeric exprn>)
Returns the cosine of the angle specified by the numeric expression. This angle must be
expressed in units of radians, not degrees. To obtain the cosine of an angle expressed in
degrees, you must multiply that angle by the constant pi/180 before taking the cosine.
ACOS(<numeric exprn>)
Returns the angle in radians corresponding to the cosine specified by the numeric
expression argument. An out of bounds error will occur if the argument is less than –1
or greater than +1. The result returned is always an angle in radians between 0 and pi.
To convert this radian result into degrees, you must multiply it by the constant 180/pi.
TAN(<numeric exprn>)
Returns the tangent of the radian angle specified by the numeric expression argument.
To obtain the tangent of an angle expressed in degrees, it must be converted to radians
before taking the tangent, by multiplying it by the constant pi/180.
ATN(<numeric exprn>)
Returns the angle in radians corresponding to the tangent specified by the numeric
expression argument. The arctangent is computable for any real argument. The result
returned is always an angle in radians between –pi/2 and pi/2. To convert this radian
result into degrees, you must multiply it by the constant 180/pi.
9-10 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
9
This section describes all functions related to character string processing and information
about strings. A character string is a sequence of characters (or bytes) that is handled as a
single data object, rather than as a multitude of separate characters. Such a data object
has various properties which can examined and manipulated: they have a finite length,
its characters are arranged in a particular order, their contents may represent words or
numbers or other abstract variable length data.
Since each character in the string is stored in an 8 bit memory byte, strings can also be
thought of as sequences of bits, otherwise known as bit strings. Each bit of a bit string
can take on one of two values: 0 or 1, which can represent Yes or No, True or False, On or
Off, membership or non-membership, or any other dual-valued relationship. Chapter 4
of this manual covers characters, strings and bit-strings in depth.
Some of these string functions return a numeric result instead of a string result, and as
such, may be used only in numeric expressions and not in string expressions. Except for
the VAL() function, which always returns a real result, all of the numeric-string functions
return integer results. Considerable savings in computation time can be realized by
avoiding unnecessary conversions between real and integer.
9-12 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
9
LEN(<string expression>)
Returns the length of the string expression. For example: LEN(“ABCDEFG”) returns 7,
and LEN(“”) returns 0. LEN() requires more execution time and memory space when
the string is not a simple string variable or constant because it has to completely evaluate
the string expression before it can determine the length.
The length of a string variable (as opposed to its dimension) may be arbitrarily set using:
LEN(S$) = <length>, where S$ is any string variable or string array element
and<length> is any integer value not greater than the current dimension of the string.
New characters created by extending the string are accessible but their particular values
are unpredictable.
REV$(<string expression>)
Returns the string expression evaluated with its characters in reverse order. For example:
REV$(“abcdefg) = “gfedcba”.
TRlM$(<string expression>)
Returns the string supplied after stripping all leading and trailing spaces. Although both
ends of the string are trimmed, you can trim the spaces from one end only by appending
a non-blank character to the opposite end, trimming the result, and then removing the
non-blank character appended. For example the following expression trims only the
trailing spaces from A$: TRIM$(“*”+A$)(2)
In order to make this capability easier to use with printable characters, any argument to
the CHR$() function can be either a number or a string. For example, CHR$(65) and
CHR$(“A”) both return the letter “A”; CHR$(48,57) and CHR$(0,9) both return the
string “0123456789”. Only the leading character of a string argument is used and
characters after the first one are ignored.
CHRSEQ$(<range>,<range>,...)
Returns a series of ascending ASCII character sequences, equivalent to a concatenation
of multiple CHR$() functions. Each <range> consists of a single ASCII value or two
ASCII values separated by a colon (:) to specify a range of ASCII values. For example, the
following two expressions are equivalent:
Chrseq$(1 ,4,65:90,255)
Chr$(1 )+Chr$(4)+Chr$(65,90)+Chr$(255)
Notice that a colon is used to separate the end-points of an ASCII range in CHRSEQ$()
functions and a comma is used as the separator in CHR$() functions. As with CHRS(),
an ASCII value in any argument to CHRSEQ$ can be specified by either a number or a
string. For example, CHRSEQ$(65:90,97:122) and CHRSEQ$(“A”:“Z”,“a”:“Z”) both
return the same result, but the string arguments make the purpose of the function much
more readable. CHRSEQ$() is provided for applications that require so many character
sequences that programming them with CHR$() is tedious and inefficient.
VAL(<string expression>)
Converts a character string representing a valid numeric value into the actual value it
represents. This is the opposite of the STR$() function described earlier. The string may
contain leading or trailing spaces (or line-feeds), but must be an otherwise valid numeric
constant. The constant specified by the string may be any legal number as recognized by
MegaBasic. This includes numbers in E-notation, as well as binary, octal and hexadecimal
notation. The rules governing the formation of numeric constants are described in
Chapter 3, Section 2, for example: VAL “ 92E3”) = 92000, VAL(“–0012.430 ”) = –12.43,
VAL(“7FFFh”) = 32767.
A numeric string may be terminated by a comma, tab, linefeed or carriage return ASCII
codes and characters beyond this point are not scanned or otherwise validated.
Therefore the VAL() argument string may contain multiple numeric fields, of which
only the first field is converted. By using the method described below, such multiple
fields can be parsed and extracted.
After converting a string to a number using the VAL() function, you can find out the
position in the string of the first character beyond the numeric character sequence
9-14 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
9
evaluated. The INDEX function returns this position right after the VAL() function is
evaluated. If an error occurred because the string contained no ASCII-represented number,
INDEX Will return zero. If all characters in the string argument participated in the
numeric sequence, INDEX will return the length of the string argument plus one. This
capability is useful for walking through a string that contains many numbers separated
by spaces or other delimiters and picking out the numeric information. It is also useful
for detecting the presence of incorrect characters in strings intended to contain a single
number and nothing else.
ASC(<string expression>)
Converts the first character of the string specified into its corresponding numeric ASCII
code. The function is the converse of the CHR$() function described below. Examples:
ASC(“A”) = 65, ASC(“9”) = 57, ASC(“”) = –1.
Asc(S$) executes much faster when the string expression consists of only a string variable
reference (indexed or unindexed), because the leading character in the variable can be
accessed without evaluating an entire string expression.
COLLAT$(<numeric exprn>)
Converts a number into a string which is suitable for sorting as a string. In many
applications, it is necessary to sort records containing some arbitrary set of fields, which
may include both numeric and non-numeric information. The COLLAT$() function
allows you to combine numbers with strings into a larger string record which, along with
many others, is searched or sorted using purely string methods and operations.
Although the STR$() function also converts numbers to strings, a string comparison
between two such numeric strings by no means implies the same comparison result of
the two values as numbers. COLLAT$() is designed to fulfill this need.
COLLAT$() converts an integer value into a four byte string, and real values into a
5-to-10 byte string, depending on the prevailing floating point precision of the BASIC
being used. Comparing two real strings will produce the same result as comparing the
two real numbers from which they came. Comparing two integer strings also compares
as if they were numbers. However, you cannot compare an integer string with a real
string and get any meaningful result because these two representations are not
compatible. Hence you must ensure that the argument data type is what you expect it to
be.
MATCH(“abcdefg”,“de”) = 4
MATCH(“abcdefg”,“DE”) = O
An optional <start> may be specified to cause the search to begin on the character
position given (skipping earlier characters of the string), for example:
MATCH(“abcdefg”,“de”,3) = 4
MATCH(“abcdefg”,“de”,5) = O
An optional <count> may be given to specify how many occurrences of the search string
to locate before returning the position found. If you specify a <count> then you must
also specify the preceding the <start>, for example:
MATCH(“This is the search string”,“ ”,1,3) = 12
MATCH(“This is the search string”,“ ”,7,3) = 19
If this repeat count is omitted, a default of one is assumed causing the function to return
the position of the first occurrence encountered. If the repeat count is higher than the
number of occurrences that exist in the target string or if it is set to zero, then a zero
result will be returned and INDEX will contain the number of successful matches found
before failing. This capability greatly improves the performance of repetitive searches
and pattern counting procedures. A typical application is the processing of packed string
fields separated by spaces or commas (as above), where direct accessed to the Nth field
or word is needed quickly.
Both the search string and pattern may be specified with general string expressions.
Sufficient internal workspace (scratchpad memory) is required to temporarily contain
both strings specified. If the <search string> is a simple string variable or an indexed
string variable, MegaBasic searches the variable directly (i.e., without copying it to
internal storage). If you specify a <search string> using any more complex a string
expression than this, it always requires scratchpad memory to hold it for searching. The
<match string> is always held in scratchpad memory while the search is in progress.
The MATCH() function is very fast and compatible with most other BASICS that include
a string search function. However, the FIND() function, described next, is much more
general purpose and, in some cases, it can provide even faster response.
<mode>
This optional parameter selects the type of search to be done, and may be one of the
following reserved words: MIN, MAX or ORD. MIN and MAX select a search for the
minimum/maximum substring satisfying the search <r elation>. ORD selects an ordered
table search for much faster response. When <mode> is omitted, the string being
searched is assumed to be an unordered table and the search simply FINDS the first
position that satisfies the comparison criterion. These modes are discussed in complete
detail later on.
<vbl$>
This parameter specifies the string variable to be searched. The result returned by
FIND() is always a character position within this string variable. The search can be
restricted to any substring within this string variable by merely indexing the variable to
the desired region within it. In such a case, the result position returned is still relative to
the beginning of the string variable, rather than to the indexed region. The variable is
9-16 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
9
always searched in-place (i.e., without moving it anywhere first). FIND cannot search the
result of a general string expression unless it is first stored into a string variable.
<relation>
This parameter specifies a criterion that constitutes a successful search. It is specified as
any one of the six comparison operators: = <> < <= > >=. FIND returns the first
position in the string variable where the comparison relation holds (i.e., is true).
<string exprn>
The <string exprn> parameter specifies the string that FIND will be searching for, and it
may contain up to 255 characters. The length of this string is not related to or limited by
the optional <step> parameter (described below). FIND() always compares this string
with a substring of equal length in the string variable being searched.
<step>
Specifies the step size to advance through the search string variable for each successive
comparison. Omitting the <step> results in steps of one byte (as done by the MATCH
function). Steps larger than one are useful when the contents of the string variable are
organized as a sequence of fixed length substrings (i.e., string records). For example a
<step> of 5 compares substrings in the string variable at positions 1,6,11,16,21,... with the
target string expression until the relation is satisfied. To search backwards through the
string variable, you can specify a negative <step>, which searches from the highest
possible comparison position in the string variable down to the first.
<count>
As in the MATCH() function, the optional <count> specifies how many occurrences of
the search string to locate before returning the position found. For example
FIND(T$>S$,5,N) returns the position of the Nth 5-byte substring in T$ that is greater
than S$.
If this count is omitted, a default of one is assumed, returning the position of the first
occurrence encountered. If the count is higher than the number of occurrences that exist
in the target string (or set to zero), then zero is returned and INDEX Will contain the
number of successful matches found before failing. See the MATCH() function for other
examples.
However, at certain times you may desire a result position which is relative to the
indexed region, rather than to the absolute string base. For this reason, MegaBasic also
computes this relative position and provides it in the INDEX function for your use if
needed. For example, after each of the uses of FIND() above, INDEX returns the
positions 8 and 13, respectively. INDEX is set in this manner for all but the ordered search
case (when <mode> is ORD). In that case, after a successful search INDEX contains the
same position returned by FIND(); after an unsuccessful search INDEX contains the
position where the search string expression should be placed if inserted into the ordered
table.
You may have noticed the similarity between FIND and MATCH. In fact, FIND(T$-S$)
returns the same result as MATCH(T$,S$). However FIND has several differences that
should be emphasized. FIND is designed to find relationships in string tables by
searching forward or backward through fixed length substrings. This generality makes
FIND() slower for certain simple byte-by-byte matching applications. MATCH is
designed for faster simple pattern matching and compatibility with similar functions in
other languages and other dialects of BASIC.
9-18 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
9
substrings, implementing your search with MIN or MAX mode will execute as much as 40
times faster. For example, the following two searches are equivalent:
Both cases search TSL$ for the highest substring, W characters in length, which is
less-than (<) the string contained in K$. As you can see, MIN and MAX mode is not only
faster but also much easier to understand within your programs. If you wanted to search
for the absolute minimum or maximum substring (i.e., not subject to any other
comparison criterion), you need only specify a comparison which will always be true, for
example:
One clever application for FIND() in MIN or MAX mode is substring sorting. Suppose
that we sorted a table of substrings by moving the lowest substring in the table to the
front, followed by the lowest of the remaining substrings, and so on through the table.
This obvious sorting method that is rarely used because it is usually inefficient to
implement. But with the FIND() function, this method is the fastest way to sort a string
table of up to several hundred substrings (i.e., other methods are faster on longer lists).
The example below sorts TSL$ into ascending order using this method:
10 For I = 1 to Len(TBL$)-W by W
20 J = Find(min TBL$(1) <= TBL$(1:W), W)
30 Swap TBL$(1:W), TBL$(J:W)
40 Next I
To sort the substrings into a descending order, just change the MIN in the FIND() to a
MAX, and change the less-than-or-equal (<=) to a greater-than-or- equal (>=). For fun, see
if you can eliminate the need for the intermediate variable J in the above sort program to
make it a 3-statement sort.
To specify that the string variable is an ordered string table and that a binary search is to be
used, you precede the search table string variable with the word ORD. The ORD mode is
well suited to applications where massive table lookups dominate the processing time.
For example a program that reads a text file to determine all the unique words and how
many times they occur can greatly benefit from using the FIND() function in ORDered
mode.
FIND() in ORDered mode has some important differences and restrictions as compared
with the other FIND() modes. These are described below along with several other fine
points:
h The sign of the <step> does not determine the direction of the search. Instead, a
negative <step> specifies that the table is in descending order, a positive <step>
specifies a table in ascending order. In any case, the <step> must be a number in the
range from –32767 to +32767.
h The only <r elation> supported in ORDered mode is equals (=). If you specify any
other <r elation> (i.e., < <= > >= <>), an Out Of Context Error will result.
h When the search for equality succeeds, INDEX is set to the same location as the
result position returned. When the search fails, the result returned is zero, but
INDEX is set to the position in the table where the string would be, had it been
found (i.e., insert it here for future lookups). This feature makes table maintenance
quite a bit easier. An example follows shortly.
h When the search table contains more than one instance of the string sought, an
ORDered FIND() will return the position of the one beginning the sequence. In an
ordered table, such occurrences will be in consecutive positions. In an ascending
table, this is the one toward the beginning of the table; in a descending table, this is
the one toward the end of the table.
h MegaBasic does not check that the substrings are perfectly ordered throughout the
table, nor does it check that the direction of the ordering is as specified. Hence,
searching an unordered table as though it were ordered will yield meaningless results.
It is imperative that you ensure that the substrings in string variables to be searched in
ORDered mode are really ordered the way you think they are, and without any
exceptions. Even one exception to this rule can easily lead to a totally meaningless result.
The example below illustrates a good model of how you go about building and
maintaining an ordered string table. It is a program that simply gets an input string from
the user, finds it in a table, inserts it if it is a new string, and increments a count if it has
been entered before.
10 Rem *** Table Insertion into an Ordered Table
20 W = 20; DIM TBL$(1000*W), COUNT(1000), STRING$(W)
30 TBL$ =; Rem -- Set TBL$ to empty
40 Input Enter string to insert -- ,STRING$
50 If STRING$ = then End; Rem -- Done when null string en-
tered
60 1 = Find(ord TBL$ = STRING$, W)
70 If I then [J = (1-1 )/W; COUNT(J) = COUNT(J)+1; Goto 40]
80 Rem -- String not found, so insert at position - INDEX
90 1=index; TBL$(1:0):=(STRING$+ *W)(1:W); Goto70
9-20 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
9
In line 10, the substring length is defined (W=20) and the string and count tables are
defined. Line 30 sets the table to empty, which ensures that trailing spaces do not
corrupt the order nature of the table. Line 40 inputs a string from the user and line 50
terminates the program if a null string was entered (i.e., only a carriage return was
typed). Line 60 searches for the input string in the table using an ORDered find(). Line
70 increments the count corresponding to the string location found in the table. Line 90
inserts a new string into the table using an insertion assignment statement (:=). Before
being inserted, the string is padded with extra spaces (as needed) so that its length is
exactly 20 characters long. It may prove beneficial to type this program into MegaBasic
and play with it for a while to get comfortable with concepts it uses.
9-22 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
9
bit range is relative to the beginning of the indexed region, rather than to the beginning
of the string variable. The following 5 examples illustrate the various ways you can
specify a bit range:
Bit positions are not the same as character string positions: for each byte position there
are 8 bit positions. The actual bit string accessed will be shorter than specified if part of
what is specified lies beyond the last byte of the string. An Out Of Bounds Error will occur
if the actual number of bits accessed is less than 1 or greater than 24. This function may
be placed on the left side of an assignment statement in order to set the bit range to
some other value. This is described in Chapter 5, Section 2 along with additional
information about the BIT() function and bit string processing.
ORD returns –1 if no bit is on in the specified bit-string. Bit positions may range from 0 to
the length of the string * 8 minus 1, which can potentially exceed half a million. Only the
portion of the specified bit rang which is actually defined in the byte string will be
searched. The following example shows how to use the ORD function to display all the
bit positions within string variable TBL$ that contain ones:
10l = –1
20 1 = ord(TBL$, 1+1)
30 If I > –1 then [ Print l; Goto 20 ]
40 End
ORD is extremely fast and permits bit strings to control other operations. For example,
bit strings can represent arbitrary record selections in large data bases. Such bit strings
can be combined and manipulated using logical operators to form complex result bit
strings which are then scanned (using ORD) to generate a report. The ORD() function
bears absolutely no relationship to the ORDered mode of the FIND() function (Chapter
9, Section 3). ORD is an abbreviation of ORDinal number, a number representing the
order of elements in logical sets.
9-24 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
9
Most file and I/O functions require you to specify an open channel number, which specifies
the file or device to be accessed. Open files and actual peripheral I/O devices can be
addressed in an identical manner, so that your program can deal with both types as if
they were identical in most cases. See Chapter 7, Section 1 for additional information
about I/O independence and redirection.
Numeric arguments supplied to these functions may be specified either as integer or real
values. Internally, however, only integer data is used and floating point arguments will
automatically be converted to integer form before they are used. These functions all
return integer results.
POS(<channel number>)
Returns the current column position of the device specified by the numeric argument.
This channel number must correspond to an I/O device or open file number (0 to 31).
Column positions range from 0 to 255. The column position is automatically maintained
by MegaBasic as characters are sent to the device (or open file) via PRINT statements,
according to the following rules:
h Set to position zero on each carriage return (ASCII 13) and by the initial OPEN
statement the made the file or device available.
h Set to the next multiple of 8 on each tab character (ASCII 9).
h Incremented by one on all codes from 32 to 255.
h Decremented by one on each backspace character (ASCII 8).
h Unchanged by all other control characters (0 to 31).
MegaBasic cannot, however, account for cursor position changes caused by special
escape sequences, function codes or system calls. Therefore POS() can be set to any
value (0 to 255) using an ordinary assignment statement. Using this feature, your
program can correct the current column position for a device whose positions have been
altered by non-standard cursor positioning. Because of the limited range of the column
position (i.e., 0 to 255), if you print a string longer than 255 characters, the channel
position will wrap around back through zero, resulting in a meaningless position when
subsequently examined.
LlNE(<channel number>)
Returns the current line position of the device specified by the numeric argument. Line
positions range from 0 to 255 change according to the following rules:
h Set to zero by opening channel D, by a PRINT statement containing a plus sign (+)
control or by a form-feed character (ASCII 12) sent to the device (except the console
#0).
h Incremented by one on every line-feed character sent to device D.
h Can be set to any value from 0 to 255 using an assignment statement. Once the
value 255 is reached, however, the LINE() position will wrap-around back to zero
and start over.
This function is useful for controlling page length while your program is generating
output. Without it, your program would have to count output lines itself. As with the
POS() function, MegaBasic cannot account for cursor position changes caused by special
escape sequences, function codes or system calls. Therefore LINE() can be set to any
value (0 to 255) using an ordinary assignment statement. Using this feature, your
program can correct the current row position for a device whose positions have been
altered by non-standard cursor positioning.
9-26 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
9
Specifies the open device or file number of the input channel. Except for
the console, printer and auxiliary devices (0,1 and 2), a channel must be
<channel> OPENed before it can be used. Unlike those that follow, this argument
is not optional.
Specifies the absolute maximum number of characters to input before re-
turning them to your program. MegaBasic allocates memory for this
<length> number of characters before the input process begins, so don’t specify
unreasonable values here to simulate indefinite input (to be terminated
by other means).
Specifies a set of characters any one of which terminates the input,
returning all preceding characters. Afterward, INDEX is set to the
character position in the <br eak set> of the character that terminated the
<break set> input. A null break set string can be specified to indicate no break set.
If the limiting input <length> has been reached without encountering
any of the characters in the break set, INCHR$() returns with all charac-
ters input and sets INDEX to zero.
A non-zero value specifies that each character is echoed back to the
<echo> channel as it is input. A zero value suppresses echoing, which is the
default when this argument is omitted.
Specifies the time in (real) seconds to wait for the next input character
before timing out and returning with all the input characters received
up to that point. The time out specifies the period between characters.
<time out> A time out of zero terminates input as soon as the next character is not
immediately available without waiting (e.g., those in the type ahead buffer).
Fractional seconds are supported to the time resolution level supported
by the host operating system.
All INCHR$() arguments except the channel number are optional. Arguments can only
be omitted from right to left (i.e., you cannot omit arguments out of the middle). This
results in five possible ways to specify INCHR$(). Examples of each form are given
below along with what they do:
INCHR$(D) Waits for 1 input character from channel D, without input echo.
INCHR$(D,L) Waits for L input characters from channel D and does not echo them.
Waits for up to L input characters from channel D, or until an input
INCHR$(D,L,B$) character matches one in B$. No characters are echoed.
Waits for up to L input characters from channel D, or until an input
INCHR$(D,L,B$,E) character matches a character in B$. Input characters echoed
if E<>O .
Inputs up to L input characters from channel D, or until an input
character matches a character in B$, or until the time between input
INCHR$(D,L,B$,E,S) characters exceeds S seconds. All input characters are echoed if E is
non-zero.
Inputs 1 character from channel D if it can be input without waiting.
INCHR$(D,1,“”,0,0) The input the character is not echoed
INCHR$() will also input characters from a file if the file is OPEN and you specify the
open file number as the channel. No end-of-file mark is checked for, although an Out Of
Bounds Error will occur if you attempt to read past the physical end of the file. Do not use
INCHR$() in this manner directly in the data list of a WRITE statement to the same file,
as it will upset the file pointer for the subsequent WRITE operation.
EDIT$
Returns the last line input from the console, i.e., the contents of the old line buffer. EDIT$
has no arguments. Whenever you execute a program, either from the MegaBasic
command level or the operating system command level, the old line is set to the
command tail. EDIT$ can therefore retrieve this command tail so that your program can
extract any additional arguments it contains. For example if you run a program from the
operating system using the command:
This command tail must be used before your program requests console input via the
INPUT statement, because the edit buffer is then overwritten and its prior contents are
lost. For testing purposes, the RUN command (in the development version of
MegaBasic) copies the text of itself into the old line buffer in a manner identical to the
above scenario.
If the program is either compiled or running under the PGMLINK system, the program
name is stripped from the front of the command tail string. You can use PARAM(16) to
decide in your program whether or not the first word in EDIT$ is the program name.
Because of the volatility of the command tail and its dependence upon the execution
model of your program, a program that will be using EDIT$ for the command tail
should begin execution with a sequence like the following:
DIM TAIL$(128)
TAIL$ = TRIM$(EDIT$)
IF PARAM(16)<2 THEN
TAIL$ = TAIL$(MATCH(TAIL$+“ ”,“ ”))
This program fragment sets string variable TAII$ to the argument string typed after the
program name without the program name portion (which normally is not needed) and
without depending on any particular execution model of MegaBasic. Subsequent use of
the command tail can then access string variable TAIL$ without worrying about any
other issues.
INPUT(<channel number>)
Returns true (1), false (O) or unknown (–1) to indicate the input status of the channel
specified. If the channel is an OPEN file number, then INPUT() returns false (O) if the
current file position is beyond the end of the file or the character about to be READ is the
endmark code, otherwise INPUT() returns true (1). Unknown (–1) is returned only
when the input status is unavailable from the host operating system or the system
returns an error for the input status request (e.g., the input status of an output-only
channel like a printer).
9-28 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
9
Do not use INPUT() to test for the end-of-file on a binary or non-text file (i.e., any file
the you are using READ and WRITE statements on). It will give false indications of
end-of-file depending on the file contents. For such files, use the comparison
FILEPOS(N)>=FILESIZE(N) to make such a test.
When the console keyboard input status is interrogated (i.e., INPUT(0)), YOU should
remember that the Ctrl-C detection system of MegaBasic swallows incoming keyboard
characters when they are typed during statement execution. Hence you should disable
Ctrl-C when using INPUT() to control programmed input from the keyboard (using
Param(1) = 1). INPUT() returns the input status without actually reading the character
present. INPUT(0) may also be spelled as INPUT (i.e., without an argument) to mean
the same thing.
OUTPUT(<channel number>)
Returns true (1), false (O) or unknown (–1) to indicate the output status of the device
specified. If the device is an OPEN file number, then OUTPUT() returns true (1) all the
time. Unknown (–1) is returned only when the output status is unavailable from the
host operating system or the system returns an error for the output status request (e.g.,
the output status of an input-only device like a 80-column card reader). OUTPUT(0),
which requests console output status, may also be spelled as OUTPUT (i.e., without an
argument) to mean the same thing.
Also under MS-DOS, the FILESIZE() function may be placed on the left side of an
assignment statement in order to set the file size to any byte length. This feature will
usually be applied to reduce a file size, since any file will be extended automatically by
the WRITE statement as needed.
To be compatible with earlier versions of MegaBasic, FILESIZE() returns the file size in
units of 256 bytes per block under CP/M, MP/M and TURBODOS operating systems. You
can however employ any block size from 1 to 65535 bytes per block by setting Param(14)
(Chapter 9, Section 5) to the size of your choice (independent of operating system).
SPACE(<drive number>)
Returns the amount of available unused disk space remaining on the disk drive number
specified. Use 0 to select the default drive, or 1, 2, 3, ... to refer to any available disk
sub-system (i.e., for drive a:, b:, c:, ...). Like the FILESIZE() function described above,
the SPACE() function returns a result which is scaled into file blocks: 256 bytes/block
under CP/M and 1 byte/block under MS-DOS. You can however control the block size by
setting PARAM(14) (Chapter 9, Section 5) to any byte count from 1 to 65535 (independent
of operating system).
DIR$
Returns the current directory pathname on the default drive. You can also select
different directories on the default drive by assigning your desired directory string to
DIR$ as an assignment statement (Chapter 7, Section 2), for example: DIR$ = “
\bin\basic”.
9-30 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
9
The purpose of this function is to provide a sequence of file names from which your
program can select and subsequently process. File directories simply become input data
for your programs. Because the names are returned in string form, arbitrary pattern
selection criteria can be imposed, i.e., you are not limited to wild-card character (? and *)
matching that is usually provided by the operating system. The following example
illustrates how one might generate a list of all files from drive B:
10 LAST$ = Dir$(“b:”)
20 While LAST$>; Print LAST$;
30 LAST$ = Dir$(LAST$); Next
MegaBasic always looks up the file name supplied and after finding it, returns the
subsequent file name from the directory. This approach permits unlimited file processing
between one file name and the next without affecting the file name sequence returned.
With proper programming, you can even sequence through the file names from multiple
disks simultaneously.
This function does have one important limitation. If the last name returned from
DIR$() is renamed or deleted, it will not be found the next time that DIR$() is called,
causing the name sequence to terminate. This can be avoided if you defer such a delete
or rename until the next file name has been extracted, or the sequence is restarted from a
prior file name known to reside in the directory.
OPEN$(<file number>)
Returns the name of the file or device OPENed under that number, or a null string if
nothing is OPENed under the number specified. File names are returned with the drive
code, full directory pathname and the file name with its extension. Device names consist
of only a name, and any drive, path or extension specified in its original OPEN is not
given in the name string because such information does not apply.
OPEN$() is useful for testing a channel number for availability (i.e., where it returns a
null string), for obtaining file names given only file numbers, for extracting the directory
path of an open file (or its drive code), and for determining whether an open channel is
a file or an I/O device (only files have a drive code in their names). OPEN$() is also
useful for generating a fully-qualified file name string that correctly identifies the file
without any knowledge of the current drive and/or directory.
9-32 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
9
This section describes various functions that provide direct access to memory and
machine ports and various internal MegaBasic operating parameters and numerous
utility functions. See Chapter 7, Section 3 for important details about Intel 80x86 memory
addressing. That section also describes other system interface facilities. Unless otherwise
specified, all arguments to these functions are best specified as integers, and numeric
results are returned as integers.
TIME$
Returns the current time, as provided by the operating system, in an 11-byte string. A
null string is returned if the time is not available from your particular system. A 24-hour
format is provided that includes the hour, minute, second and 1/l00s seconds (e.g.,
12:34:56.78). The first character of TIME$ is a blank character whenever there is no
ten-hour digit. Since TIME$ is a string, you can easily create any other time format with
further string operations.
DATE$
Returns the current date, as provided by the operating system, in an 8-byte string. A null
string is returned if the date is not available from your particular system. The date is
provided in month/day/year format with two decimal places for each value. The first
character of DATE$ is a blank character whenever the month can be expressed in one
digit. Since DATE$ is a string, you can easily create any other date format with further
string operations.
ELAPSE [ ( <mode> )]
Returns the number of seconds that have elapsed since the last time that ELAPSE was
called. The first time it is called, ELAPSE() returns the number of seconds since
00:00:00.00 (midnight of the previous night). You can specify a non-zero argument to get
the time without resetting it for subsequent calls. Normally, ELAPSE is called without
specifying any arguments (e.g., X = ELAPSE).
ELAPSE returns a real result, which may include fractional seconds down to the
millisecond level depending on the time capabilities provided by the operating system.
Under MS-DOS, the precision of ELAPSE() resolves down to the 1 millisecond level.
However, do not rely on this level of accuracy when running MegaBasic under Microsoft
WINDOWS because the timing base is unpredictable. ELAPSE returns zero under
systems that do not support a usable time-base.
ARGUMENT
Returns true (1) if any open-ended arguments (Chapter 8, Section 4) remain to be read from
the argument list of the current subroutine call, or false (O) if none.
9-34 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
9
where X and Y are real and I is integer. In this example, MegaBasic might convert Y to
integer (depending on its value) so that the multiplication can be performed using the
much faster integer multiply. Upon completion, however, the integer product would be
converted back to real mode to be added to real X. Therefore we are forcing I to real
mode to prevent the unnecessary conversions from slowing down the computation.
ERRTYP
Returns the error code of the previous error encountered and ranges from O to 255. See
the ERRSET statement in Chapter 6, Section 4 for additional information. Appendix A
lists all the MegaBasic error messages and error types.
ERRMSG$
Returns the error message of the most recent error encountered. This is especially useful
for informing the user of errors that the program has trapped. See the ERRSET
statement in Chapter 6, Section 4 for additional information. Appendix A lists all the
MegaBasic error messages and error codes.
ERRPKG$
Returns the name of the package (in string form) in which the most recent error
occurred. See the ERRSET statement in Chapter 6, Section 4 for additional information.
ERRLINE
Returns the line number of the line on which the most recent error occurred.
ERRLINE(1) returns the relative statement number on the line where the error occurred.
See the ERRSET statement in Chapter 6, Section 4 for additional information.
ERRDEV
When an error occurs when using some OPEN file or device, your program may need to
know which device was involved in the error. ERRDEV always returns the channel
number of the device most recently in use before or when the error occurred. For
example if you invoke the file size function: FILESIZE(l9) and no file is OPEN under
file number #19, a File Not Open Error will occur. If your program traps this error,
ERRDEV can be invoked to return the offending file number, in this case a value of 19.
ERRDEV is only of use when you trap errors using the ERRSET statement, because your
program will immediately terminate if any error occurs and no error traps are in effect.
See the ERRSET statement in Chapter 6, Section 4 for additional information.
INDEX
Returns secondary information produced as a side-effect after invoking a number of
other MegaBasic statements and functions. A brief description of these secondary results
is given below, but for further details you should consult the complete documentation of
the operations it involves.
If you intend to use INDEX after some operation, use it right away or store it in a variable
because of the likelihood that it may be altered by a subsequent operation (i.e., one of
the above) before you use it.
9-36 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
9
FREE(<numeric exprn>)
Returns a variety of memory resource statistics, as selected by the numeric argument:
EXAM(<memor y address>)
Returns the value of the memory byte at the address specified by the argument
expression. Note the distinction between this and the EXAM statement (Chapter 7,
Section 3). The memory address may be specified as a segment:offset pair as described in
Chapter 7, Section 3.
SEG(<variable name>)
Returns the actual 80x86 segment address of either the default segment setup by a SEG
statement (if no argument is specified), or of a variable specified to the SEG() function.
If supplied, the variable must be given as a variable name, unadorned by indexing or
subscript specifications. This is useful for accessing the memory allocated to variables
with the FILL, EXAM, and CALL statements.
[<variable name>l
Returns the memory offset address of the variable specified, which may be a string or
numeric scalar or array element. Offsets of integer or string variables refer to the first
byte of the given integer, string or indexed string variable. Integer values are physically
stored in memory low-byte first. Offsets of floating point variables refer to the sign-byte
of the value, which is the last byte of the number. Variable offsets can be used in CALL
statements for passing pointers to data to be processed, or in FILLing or EXAMining
their memory contents directly from your program.
[V] returns only the offset portion of the two-component 80x86 cPu memory address. The
segment portion is available as a default segment using the SEG statement (Chapter 7,
Section 3), and from the SEG() function described above.
For example, ENVIR$(“PATH”) returns the directory search path string, and
ENVIR$(“COMSPEC”) returns the full path name of the command shell file. These
names must be fully spelled out in upper case with no spaces, equals signs (=) or other
extraneous characters. ENVIR$() returns a null string ( ) if the specified name string
does not exactly match any existing string <name>.
Environment strings contain information about alternate sub-directory search paths and
other information communicated to all programs. The MS-DOS SET command is used to
build this set of environment strings and display them on the console. Your program can
then use these strings to select the directories from which files are accessed, and base its
decisions on the prevailing parameters provided in the environment. See the SET and
PROMPT commands in your MS-DOS operating system manual for further information.
ENVIR$(0) returns the pathname of the main program of a MegaBasic application. This
makes it possible for an application to access files and packages from the same directory
that the main program was loaded from. You can also use it to alter program behavior
based on the actual name of the main program.
There are some key differences between the interpreted and compiled versions of
ENVIR$(0) that you may need to know. The interpreters (BASIC or RUN) always return
a fully-qualified pathname (i.e., with the drive, directory path, name and extension) of
the actual main program file. If a LINK statement is executed, the new main program
brought in by the LINK will then be returned by ENVIR$(0).
On the other hand, from compiled programs ENVIR$(0) returns the pathname of the
original .exe file executed from the command shell. If you run .go files with the runtime
libraries explicitly, ENVIR$(0) will return the pathname for the runtime library file
instead of the compiled main program. Furthermore the compiled ENVIR$(0) result is
unaffected by subsequent program LINKS, while under the interpreter it is.
9-38 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
9
PARAM(<parameter number>)
Returns an internal MegaBasic condition selected by parameter number, ranging from 0
to 25. Each Param is explained below. Certain Params may be set with an assignment
statement, such as: Param(1)=0. Such Params are shown marked with an asterisk (*).
Additional Parameters may be added to this list from time to time.
9-40 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
9
9-42 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
Chapter 10 Multiple Module Programs
10 section level 1 1
figure bi level 1
table_big level 1
Suppose that you could collect all your favorite functions and procedures and somehow
make them available as additional primitives of the language, extending its capabilities
and expressiveness accordingly. And further suppose that there was no limit on how
many of these primitives you could add to the language, as long as the total memory
resources permitted them. Given enough built-in features in any language it is easy to
see that any application could be implemented by a small program. This is the
philosophy behind MegaBasic packages. All MegaBasic package concepts and supporting
statements are described in this section, as summarized below:
Although MegaBasic packages are normally written in MegaBasic, you can also develop
packages using low-level assembly language as well. Assembler is the fastest possible
computer language to implement software in, but it is also one of the more difficult and
demanding languages to write in. MegaBasic assembler package development is an
advanced capability covered in Chapter 10, Section 5. However, using assembler packages is
virtually identical to using normal MegaBasic packages, so you need to understand the material
presented below regardless of the package implementation you choose.
GFK-0256 10-1
10
When you call a general purpose subroutine, it should not be able to produce
unexpected side-effects on your program. Imagine the chaos of developing a huge
program where all variable, procedure and function names are accessible throughout the
entire program. Indeed, such a situation exists in all BASIC programs and this is the
major reason that BASIC has historically been unsuitable for implementing large
complex systems.
MegaBasic package mechanisms have been designed to overcome these limitations and
to provide a flexible environment for large-scale application development. MegaBasic
packages let you create subroutines in such a way that all their implementation details
are hidden from the program that uses them. Further, the controlled interface between
MegaBasic packages greatly simplifies the development of special-purpose packages
that can be easily integrated into specific systems in an independent way.
The use and definition of packages centers around a small set of primitive operations
which, in conjunction with one another, provide all the facilities to load, access, detach
and remove packages and the structures they contain all during execution. We shall
begin by discussing just what makes a package.
10-2 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
10
The statements covered in this section are used for combining programs into larger
programs which may, during execution, reside totally in memory, or partially in memory
and partially on disk files. A single program module is limited in MegaBasic to a
maximum of about 75,000 bytes, enough space for up to 3 or 4 thousand statements.
However, up to 64 separate programs can co-exist in memory, limited only by the total
memory installed in your machine and made available to MegaBasic. Under program
control, such program modules, called packages, can be brought into memory and access
relationships established among them, permitting each package to access the subroutines
and data of others in a controlled manner. The statements discussed in this section are
summarized as follows:
Access between packages is limited to named procedures, functions and variables, all of
which must be explicitly declared as SHARED in order to be externally accessible.
Subroutines and variables that are not declared SHARED can only be accessed from
within the program in which they are defined, completely hidden from the view of all
outside packages.
This multiple program model is simple to use, yet more powerful and general purpose
than the CHAINing facilities of other BASICS (and of MegaBasic). We highly
recommended that CHAINed programs be upgraded to the package model and to avoid
LINK statements in new programs. In so doing, future enhancements to your programs
will be much easier to implement and programming side-effects from major changes can
be minimized and controlled.
you should also understand. See Chapter 10 for additional information about developing
large multiple-module programs.
This statement is really just an extension of the data type declarative DEF statement
(Chapter 5, Section 1). You should refer to that discussion for important additional
options that can be used to declare SHARED variables as string, integer or real variables.
DEF statements are not executable but processed just before the program begins
execution, and their order can affect the data type of SHARED variables.
The sequence in which package files are INCLUDed is important because the prologues
are executed in that order. Further, this order also controls the sequence in which
epilogues are performed when the program ENDS. The ACCESS statement (below)
performs an implicit INCLUDE operation on every package it deals with, hence INCLUDE
statements are useful primarily to control the order of subsequent prologue and
epilogue execution.
Although in memory, packages cannot access anything defined in other packages until
access has been explicitly granted with an ACCESS statement (described next). INCLUDE
is not generally a frequently needed statement because ACCESS always implicitly
INCLUDES packages not already memory resident.
10-4 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
10
10-6 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
10
The LINK statement physically frees all packages in memory as it chains from the current
program to the next program (including the old program just left). These remaining
packages are neither active nor ACCESSed by anyone, but subsequent ACCESSes or
LINKS to them will execute quickly because they don’t have to be re-loaded from the disk
again, similar to the DISMISS statement operation. To force a program to be loaded from
the disk, you can flush all inactive packages from memory by executing a FREE statement
(no arguments) just before executing the LINK, ACCESS or INCLUDE.
If the target LINK program is already in memory, it too is executed without being
re-loaded from the disk. If the LINK program does have to be loaded from the disk,
MegaBasic requires enough free memory to hold the new program without first freeing
the program executing the LINK.
To further support systems that LINK from program to program, each of which
ACCESSing many of the same supporting packages, a special LINK mode can be
enabled by setting PARAM(25) to 1. This causes subsequent LINK statements to leave
supporting packages initialized with all their variables, open files and ACCESSes intact.
The subsequent program still has to ACCESS the packages it needs, but nothing is
automatically DISMISSed by the LINK statement in this mode. Variables may be passed
to the next LINK program the usual way (i.e., listed individually or @ for all variables).
PARAM(25) can be turned on and off at any time during execution to affect all subsequent
LINK statements executed.
A package is just a program stored in a file that you develop just as you would any other
program. In addition, it contains a few declarative statements for making some of its
defined objects externally known and accessible. If you already have an ordinary
MegaBasic program, you can turn it into a MegaBasic package by understanding and
applying the following four logical components of a MegaBasic package:
SHARED Objects
It would greatly reduce the power and effectiveness of packages if everything they
contain was always externally accessible. Therefore, subroutines and variables within
packages are not externally accessible unless they have been specifically declared as
SHARED objects. To declare functions and procedures as externally accessible, insert the
word SHARED in their corresponding DEF statement, for example:
Without defined as SHARED, these subroutines would be available for use only from
within the package they are defined. To declare SHARED functions as having a string,
integer or real type, you should place the word STRING,
INTEGER or REAL preceding the FUNC reserved word, as described in Chapter 8, Section
1. To share variables with external programs, each variable must be declared in a DEF
SHARED statement:
The <variable list> consists of variable names separated with commas. You may have as
many of these statements as you need to list all the desired SHARED variables and they
may be located anywhere in the package program. This declaration is necessary only for
the package that owns the data. See the discussion in Chapter 5, Section 1 for further
information on DEF statements. External programs refer to SHARED variables just as if
they were defined locally.
10-8 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
10
Do not declare the same name in two different packages as SHARED, or a Shared Name
Conflict Error will occur when one package accesses the other or when both are accessed
by a third package. This error also occurs when an external name is already defined
locally for another purpose. Declare SHARED objects just once in the package that owns
them. Then, any package that requires access to the name can ACCESS (Chapter 10,
Section 1) the package that owns it.
Line-numbers and line-labels and GOSUBS cannot be SHARED. Subroutines of the FUNC or
PROC variety are the only program objects that may be executed from another program.
Specifically, this means that a program cannot jump into another package, except as part
of a call and return sequence.
An important application for SHARED variables is implementing a set of data which is
available for reference throughout the entire program (i.e., by all packages). Known in
other languages as GLOBAL or COMMON data, this type of access is frequently required
when developing a large program. Truly global data should be kept in one specific
package that contains nothing but global objects (variables and/or subroutines), rather
than along with other SHARED objects which are not really global (i.e., needed by only a
subset of the system). By collecting all global variables and subroutines into one package,
they are easier to manage, control and access by all other packages. See the ACCESS
statement (Chapter 10, Section 1) to see the various ways for making SHARED data
externally available.
executed only after all accesses to its package have been released using DISMISS
statements. An epilogue will not execute until the prologue (if any) has successfully
completed and returned, so that if the prologue aborts due to an error, the epilogue will
never execute.
PARAM(21) controls how errors are reported in packages. If subroutines in your
package detect errors in the way they were called, it can be desirable to report such
problems as errors in the use of the package. By setting PARAM(21) to 1 (see Chapter 9,
Section 5) in your package PROLOGUE, errors will be reported as errors in the calling
reference to the subroutine (i.e., in the calling program), instead of in the package itself.
10-10 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
10
Once packages are defined, they may be used by any program which requires them. To
use a package, its program file must reside in memory along with the program that will
be using it, and its SHARED objects must be known by that program. Since memory is
generally a scarce resource, packages that have served their purpose and are no longer
needed can (and should) be removed from memory to make room for other packages or
data. The various facilities that provide these functions will now be described.
Accessing Packages
In order to make the SHARED contents of a package available to your program, two actions
must be taken. First, the file containing the package must be loaded into memory and
initialized. Second, all of the SHARED names it defines must be made accessible to your
program, so that when you refer to one of these names, the external object associated
with this name is accessed (instead of creating some new, local program variable). This
entire process can be done by the following statement:
ACCESS <package name>
where <pack age name> is specified as a string expression that evaluates to the file name
of the MegaBasic package. There are other options on the ACCESS that we will describe
shortly, but first, let’s explore what happens when MegaBasic executes the ACCESS
statement above.
First the package file name is located in the directory and the program is loaded into
memory. An error results if the file cannot be found. If the package is already present in
memory, MegaBasic saves time by not reloading it from the disk. After loading a package
and initializing its DEF statements, The ACCESS statement then binds every name in
your program that matches a SHARED name in the package to the object having that
name. Only those SHARED names that actually have references are bound. If any of the
matching names are already associated with some variable or subroutine in your
program (i.e., they are already defined), then the ACCESS fails and MegaBasic reports a
Shared Name Conflict Error along with the name and packages involved. Finally, the
prologue procedure within the package is invoked if one has been defined. A prologue is an
ideal place for additional ACCESS statements if the package being initialized requires
additional packages for its own operation.
This finishes the ACCESS statement, and execution continues to the next program
statement following it. In summary, an ACCESS loads, initializes and connects a package
to your program or other package. It creates only a one-way connection, so that the
SHARED objects are accessible in a controlled, limited way, rather than to all packages
everywhere.
As mentioned earlier, ACCESS statements have some other options to give it more
flexibility. In particular, one ACCESS statement can access a list of one or more packages.
You can also specify another list of packages that accesses the first list. An ACCESS
statement can take one of the following three forms:
where <list> is a sequence of one or more package names separated by commas, each
specified by a separate string expression. If any specified package name is not present in
memory when an ACCESS statement is invoked, MegaBasic will load and initialize it
automatically from the disk. Once ACCESSibility has been established, further
identical ACCESS requests are ignored and no error reported.
Your package may use any SHARED objects that belong to other ACCESSible packages,
just as if they were defined directly within your program. Thus your programs may be
written using much higher level constructs than simply the built-in primitives provided
in the language. The details of these constructs are hidden from the view of your
program, greatly simplifying your programming design and implementation tasks.
Including Packages
The load-initialize sequence performed by ACCESS can also be performed without
binding the SHARED names within the package to any package or program. This is done
using the INCLUDE statement, which takes the form:
INCLUDE <list of package names>
where <list of package names> is a list of file names specified with string expressions,
separated with commas. An error results if any file cannot be found. Packages already
present in memory will not be reloaded from the disk. Package names in this and all
other package statements are specified as file name string expressions. Once a package
has been INCLUDed into memory it may be accessed by other packages using the
ACCESS statement.
10-12 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
10
where <listl > is a sequence of currently ACCESSed package names, separated with
commas. The optional <list2> specifies the packages from which the first list is
DISMISSed. Any package names listed that are not currently in memory are ignored. By
omitting <list2>, all <listl > packages are DISMISSed from the package executing the
DISMISS statement.
The DISMISS statement does the reverse of the ACCESS statement. The effect is that
ACCESSible packages become inACCESSible from the program or package that
previously accessed them. When a package is no longer ACCESSible from any package,
MegaBasic automatically removes it and its variables from memory. This released
memory space becomes immediately available for reuse elsewhere. One DISMISS
statement can break the ACCESSibility of (potentially) many pairs of packages.
The DISMISS statement performs a specific sequence of operations on each ACCESS
relationship to be broken. The sequence is performed the same way on each pair, and it
consists of the steps described below. Given that program MAIN has previously accessed
package LIBRARY, the following sequence of operations is performed:
h All references to the LIBRARY from MAIN are broken. Subsequent reference to
these in MAIN package will be treated as new default variables. MAIN no longer
ACCESSes this package.
h If LIBRARY is still accessed from other packages no further action is performed.
If LIBRARY is not ACCESSible from any package, its epilogue routine is executed
(if present). Epilogues are a convenient place to close working files and perform
any general clean-up procedures necessary or additional DISMISS statements, if
the package was ACCESSing any private packages of its own.
h All LIBRARY data, SHARED or otherwise, is dismantled and its allocated memory
is released back into the system for subsequent reuse for other purposes.
DISMISS does have some restrictions, all of which stem from the need for SHARED
functions and procedures to be able to RETURN. A package cannot DISMISS itself. If it
could, then upon finishing the DISMISS statement there would be no program to
continue with. For the same reason you can’t DISMISS the original main program and
MegaBasic reports an Illegal Package Operation Error if such attempts are made.
When DISMISS removes a package from memory, its program source image actually
remains intact, but the region it occupies is marked as free memory. If another LINK,
INCLUDE or ACCESS statement requests its presence before it is actually overwritten,
MegaBasic will recover and reuse the code already in memory, rather than spend the
time to reload the program from its disk file all over again. If your program needs
memory for variables or arrays, these free areas are used if no other free memory is
available.
Orphaned Packages
A package will not go away until you explicitly DISMISS it and nothing else ACCESSes it.
Although this sounds simple, it is easy to assume that when packages are DISMISSed,
their ACCESSes will magically get DISMISSed as well. But instead, such orphaned packages
will just sit there in memory, with all their variables intact and their epilogues unexecuted,
taking up memory space until they are formally DISMISSed. If the package is
ACCESSed again, then it will still be initialized, so its prologue will not be executed and its
SHARED variables will have whatever values they had at the time they were last
modified. Orphaned packages show up in the SHOW command as Detached.
This behavior gives you a good deal of control over the lifetime of a package and its
variables. For example, a package of global variables would normally never be
DISMISSed so that it would remain active throughout execution regardless of whether it
was actually ACCESSed by others at any particular instant in time.
In order to accommodate those applications that really do have to DISMISS all the
currently unACCESSed packages, you can still do so by specifying a DISMISS statement
without any arguments (i.e., DISMISS). However, avoid using this statement while inside
a prologue, directly or indirectly, because it can prematurely DISMISS packages that are
in the process of being ACCESSed at that moment.
A package can guarantee that it survives a DISMISS (including one with no arguments)
by simply ACCESSing another package that in turn ACCESSes it back. Unless an outer
package performs a DISMISS package FROM *, such mutual ACCESSes ensure that both
packages always remain ACCESSed by at least one package, preventing their removal
(and their epilogue execution) from occurring (except by the mechanism described below).
10-14 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
10
no epilogue executed! In fact, the epilogue cannot be executed without MAIN explicitly
dismissing the two packages from each other. This is because epilogues are normally
executed for packages being dismissed that are accessed by no one else. Thus breaking up
a package in this manner may actually require changing all accessing packages, an
undesirable characteristic from a maintenance standpoint, particularly in large systems
of packages.
To enable an epilogue to dismiss such local networks of sub-packages, you can execute
epilogues in packages with active accesses by raising its access count threshold with the
DISMISS function (no arguments). For example, setting this threshold to 5 (e.g.,
DISMISS=5) means that as soon as the package is DISMISSed and 5 or fewer accesses
remain, its epilogue is executed. This threshold can only be set or examined within the
current package and defaults to zero if not set. The way you would use this is to set
DISMISS (in the prologue) equal to the number of ACCESSes that would normally be
active when the package was no longer in use by any external packages. In our example
above, you would set DISMISS=1 so that the epilogue of PKG-B would execute as soon
as PKG-C remains its only accessor.
To assist package management, the ACCESS function (also no arguments) returns the
number of packages that currently access the current package. One use for this is to set
DISMISS = ACCESS in the prologue after all its sub-packages have been accessed and
initialized. This would cause its cleanup epilogue to execute as soon as an outer package
dismissed it, even though one or more of its sub-packages still accesses it at the time. For
such a mechanism to operate effectively, the epilogue must dismiss all its sub-packages
so that upon return from the epilogue, all accesses to the package are cleared and the
system can then release the package from memory. Packages are only freed when no
other packages are accessing it (i.e., when ACCESS = 0), no matter what you set DISMISS
to.
Access-counts are incremented on completion of the package ACCESS, i.e., after the
prologue executes. So calling the ACCESS function in a prologue returns an access count
that does not include the ACCESS in progress and in fact will always be zero upon entry
into a prologue. Conversely, access-counts are decremented at the start of the package
DISMISS, i.e., before the epilogue executes.
MegaBasic packages are extremely general and their facilities may be applied in an
unlimited variety of configurations. Having so many different ways of solving the same
problem can be initially confusing when learning how to use packages. It is therefore
useful to have some simple model upon which to base your beginning approaches to
system development. Once experience is gained in using packages with a simple model,
the subtle nuances can be more fully explored and applied to your implementations.
This model is conceptually very simple and yet provides a means to execute programs
much larger than the available memory. However, let’s further suppose memory was
sufficient to contain your entire program, but you had so many packages loaded at the
same time that the overall complexity became unmanageable. This important problem
can be overcome if each of the packages ACCESSed above were to fit the following form:
h The prologue routine contains additional ACCESS statements to support itself by
accessing its own packages.
h The epilogue DISMISSes all packages INCLUDed by the prologue.
This use of prologue/epilogue routines permits many packages to appear as one package,
which has obvious simplifying implications. To do this effectively, each package should
contain objects of roughly the same level of abstraction. The original main program
would perform only the highest levels of processing, invoking primarily objects defined
by other packages which in turn would be defined at lower levels in other packages
until, at some point, all details were processed.
Menu Driver
Database
management
10-16 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
10
Menu Driver
This package is the highest level of the system, although it may be just another package
in an even larger system (e.g., as a menu sub-system). It will have an initialization
sequence implemented as a PROLOGUE which ACCESSes all the packages that it needs
for its own processing. Since MENU provides a user interface to the transaction processing
and reporting facilities of this system, its initialization prologue will include the statement:
ACCESS “TRANSACT”, “REPORT”, “GLOBAL”.
Transaction Processing
TRANSACT is the package handling the various transactions that the user is allowed to
make. It consists of a set of subroutines which are called from the MENU package and is
shielded from having to know about higher-level details. When initialized, TRANSACT
requires access to the global data area used throughout the system and to the data base
manager. Its prologue therefore contains the statement:
ACCESS “GLOBAL”, “DATABASE”
Report Generator
REPORT performs all of the display, listing and visual output of this system. Because of
this it needs the services of the data base manager (DATABASE), as well as to the system
global data package (GLOBAL) and a special graphics package designed to drive the
various video and plotter hardware that may be present on the system. Its initialization
prologue will therefore contain the statement:
ACCESS “DATABASE”, “GLOBAL”, “GRAPHICS”.
File Handler
This package provides the routines for converting high-level file requests from
DATABASE into low-level system-specific file transfers. By changing this package, the
entire system is able to move to another system with different file conventions (e.g.,
CP/M to UNIX). FILES does not require other packages for its operation, but its
initialization prologue might open files and set up data structures so that subsequent
service requests can be filled immediately.
10-18 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
10
The biggest problem you are likely to encounter in converting a LINKed system into a
package system, is that you must always RETURN from the PROLOGUE level in order to
cause the next module to be executed. LINK statements, however, can be executed at
any subroutine level to bring in the next module. Low level LINK statements will
therefore have to be re-implemented in such a way as to meet the RETURN level
criterion. By restructuring your program module so that all LINKS are performed at the
top level of the module (i.e., no LINKS within procedures, functions and GOSUBs), the
job of converting to packages will be trivial. Such a change in structure will have the
additional side-benefit of forcing the intermodule interface to be centralized at the top of
the program, improving the maintainability of the program as a result.
10-20 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
10
With more than one program source in memory at one time, the workspace
environment becomes a collection of workspaces, one for each package. Within each
workspace, you may alter and debug the source code it contains without altering the
contents of any other workspace. It is only after you switch to another workspace that
you may apply program development facilities to another program source.
There are two kinds of workspaces: temporary and permanent. Workspaces created by
LINK, ACCESS and INCLUDE statements are temporary, because the RUN command
eliminates them just prior to beginning program execution. The LOAD command may be
used to create and fill permanent workspaces, i.e., those which survive a RUN. Permanent
workspaces persist until they explicitly eliminated using the CLEAR command. If you
switch to a temporary workspace and modify its contents, it becomes a permanent
workspace. When saved to a file, such a workspace reverts back to temporary status. This
scheme let MegaBasic protect and maintain the source files you are actually working on
while automatically eliminating unnecessary source files from your memory space.
When automatically eliminated, workspace contents is merely marked free. If the
memory space occupied by freed programs is later needed by other activities, MegaBasic
physically releases them to obtain the required memory. Such programs will reside in
memory indefinitely if their occupied memory is never needed. If such programs are
again ACCESSed before released, no disk transfers are required because the program is
already memory resident. Therefore, by careful design, you can implement
multi-package systems which automatically adapt to the amount of memory available in
the host computer. Given enough memory, all packages will stay resident; with
somewhat less memory, occasional disk activity will occur to bring back a temporarily
DISMISSed package.
Each workspace carries the name of the package it contains. You can see a summary of
all workspaces by issuing a SHOW command (Chapter 2, Section 5). Each workspace
name is displayed along with the package size and the amount of data currently owned
by the package. The currently selected package is always displayed at the top of the list.
To see the ACCESS relationships involving the current package, use the SHOW ACCESS
command (Chapter 2, Section 5).
Several operational parameters are locally maintained for each workspace to facilitate
their independence. The auto-SAVE file name is unique to each workspace since each
package comes from a different file. The Ctrl-C state, disabled and enabled by
PARAM(1), is also separately defined for each package. Likewise, the TRACE mode is set
up on an individual basis so that all debugged packages can TRACE as if they are built
into the language (by not tracing). To more fully clarify the package environment, a
complete list of facilities common to all packages is contrasted below with those facilities
which are maintained independently:
The USE command may also be typed with a package name in order to directly select your
desired workspace without having to sequence through all the names available. If the
supplied package name is found among those present in memory then it will be
immediately selected. If it is not found, a new empty workspace can be created under the
name given.
When a program stops for any reason (i.e., END, STOP, errors), the currently selected
workspace is set to that package which contains the code in which the stop took place.
This is most convenient for debugging purposes and eliminates the need for explicitly
selecting (USE) packages in many instances. MegaBasic displays the current package
name whenever it automatically changes the workspace. Whenever you LOAD a package
into a workspace, it becomes the currently selected workspace.
10-22 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
10
Unfinished Epilogues
When a multi-package MegaBasic program terminates prematurely, due to a Ctrl-C or
untrapped error interruption, you can always enter a direct END statement from the
keyboard to execute all the remaining epilogues. However this step is easy to forget and,
under some circumstances, execution of the package epilogues may be vital to
subsequent program operation (e.g., epilogues may release important system resources).
Therefore, MegaBasic detects situations where epilogue closure may be disrupted and
executes an implicit END statement before continuing on to perform certain commands.
This occurs when there is at least one remaining epilogue and you type a LOAD (into an
existing active workspace), RUN, CLEAR or BYE command.
There is about 500 bytes overhead in each executing package, so it generally take less
memory to have fewer larger packages than numerous small packages. Executing your
application under the RUN version of MegaBasic reduces this overhead further.
10-24 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
10
This section describes the structures and protocols required by MegaBasic packages
developed in assembler. It assumes that the reader already understands both the
concepts involved in using MegaBasic packages and how to write programs in 80x86
assembler. Use of MegaBasic assembler packages is virtually identical to using MegaBasic
interpreter packages. Their highlights and differences are summarized below:
h Assembler packages are written in machine code using an assembler or possibly
a high-level compiler language, such as C. Because of this, the operations such a
package supplies are extremely fast.
h Assembler packages contain procedures and functions invoked from your
MegaBasic applications which support a flexible argument structure for
communicating with the calling program. SHARED variables are not supported
by assembler packages.
h Assembler subroutines are invoked with names and argument lists instead of
CALLS, PEEKS and POKES, making them as easy to use as the built-in functions
and statements of MegaBasic.
h Assembler packages are ACCESSed, INCLUDEd and DISMISSed from
MegaBasic programs just like interpreter packages. They can also be included in
binary load images created by the PGMLINK facility that comes with the
MegaBasic RUN version.
h Unlike interpreter packages, an assembler package cannot ACCESS any other
MegaBasic package. It should be regarded as a completely self-contained library
of machine-coded subroutines.
h The SHOW command will display the names and sizes of all assembler packages
(along with all other packages) currently loaded, and they are shown with the
type Binary. Obviously, such packages cannot be listed or modified and therefore
you are prevented from getting into these workspaces with the USE command.
h Assembler packages have to be written very carefully and all the rules for
constructing them must be followed exactly. Programming errors in these
packages will, more often than not, crash the system. Furthermore the
debugging environment MegaBasic provides to interpreter programs does not
apply to assembler packages. In short, you are on your own.
h Additional support for assembler packages includes assembler source code for
an example package and several utilities to assist your efforts in creating,
checking and listing assembler packages.
Assembler packages are accessed from MegaBasic programs using the ACCESS
statement or the automatic package mechanism (see the CONFIG utility). ACCESS
performs roughly the same sequence of operations to link both package types to a
MegaBasic program. This process can be summarized as follows:
h Look up the specified package name in the set of packages already loaded. If not
in memory then look up the specified package name in the file directory. If it is
found then load the package into memory, otherwise report a File not found
error.
Once ACCESSed, all the procedures and functions that have been implemented by the
package are immediately available, and can be invoked by name from programs or from
direct-statements typed at the keyboard. The rules for invoking procedures and
functions are simple and direct:
Arguments can be values (i.e., expressions) or variables (i.e., passed by address). Any
argument can be defined as optional, so that the calling reference can leave off some or
all arguments depending on the application. Optional arguments can be omitted
anywhere in the argument list, not just from right-to-left as in interpreter PROCS and
FUNCS. For example the function call TEST(FIRST,,THIRD,,FIFTH) omits the second
and fourth arguments. The extra commas are only required when a specified argument
follows an omitted argument (e.g., the TEST() function above might have more than
five arguments defined).
h At the beginning of the file is a package header, usually 64 bytes. This header
identifies the package to MegaBasic and defines certain necessary information
for loading and executing it.
10-26 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
10
h After the package header, are one or more FAR subroutine blocks that
implement each of the PUBLIC entry points provided to the calling MegaBasic
program. Each block consists of a subroutine header and the assembler code that
implements the subroutine.
h The entire package file must be less than 64k bytes in length. Although this size
may seem limited, a tremendous amount of machine code can fit in such a
space. MegaBasic itself is not much larger than this.
The package header identifies the package as a MegaBasic assembler file and includes a
minimal amount of other global information. The header is defined in the INCLUDE file
supplied with the MegaBasic developers tool kit (ASMDEFS), which currently defines
the following fields:
Field
Offset Package Header Field Contents
0000 Two bytes containing the hex values 04h, 00h to help MegaBasic discrimi-
nate between the various types of package files that can be
loaded (i.e., interpreter files, ASCII files, assembler packages, etc.).
0002 WORD containing the number of bytes in the header.
0039 25 bytes reserved for internal use and future expansion and must be
set to zero.
The FAR procedure blocks that immediately follow the header each consist of a
procedure header, the procedure name string, and an assembly code procedure that
implements the desired operation. Each of these blocks should begin on an even offset
for performance reasons, but this is not mandatory. The procedure header defines the name
of the procedure, its type (i.e., procedure or function), and its argument structure. The
procedure header specification is defined as follows:
Field
Offset Procedure Header Field Contents
0000 WORD containing the offset of the next procedure block header.
Zero indicates that no more procedure blocks follow. Non-zero
values are relative to the beginning of the package header.
0002 WORD containing the offset of the procedure code entry point.
This is where MegaBasic enters the assembler procedure.
0004 WORD contains the offset of the procedure name. Names are de-
fined later on.
0006 WORD defining the procedure type: procedure or functions.
Functions may return integers, reals and strings. The legal values
for this field are: PROCED, IFUNC, RFUNC, SFUNC, PROLOGUE
and EPILOGUE. These are defined in a special INCLUDE file
(ASMDEFS.asm) provided with the MegaBasic developers tool
kit.
A PROLOGUE is just a procedure that is automatically executed
when the package is initially loaded. An EPILOGUE is a procedure
that is automatically invoked when the package is removed (using
the MegaBasic DISMISS statement). You should not declare
more than one subroutine as a PROLOGUE or an EPILOGUE.
0008 WORD defining the maximum length of the result returned from a
procedure of type SFUNC. It should be sel to zero for all other
procedure types. This value, a byte count, causes this much
memory to be reserved on the logical control stack for the
returned string function result.
0010 3 WORDS reserved for possible future use.
0016 WORD specifying the maximum number of arguments defined for
the procedure. Zero may be specified tc indicate no arguments.
0018 Zero or more WORDS defining the argument types from left to
right. The number or words defined here must be the same as the
count defined in the preceding field. The permissible values for
this field are defined in an INCLUDE file provided with the
MegaBasic developers tool kit. The labels it defines are described
below.
The procedure name, referenced by the procedure header, consists of a length byte
followed by the name characters (in upper case) and terminated by a carriage return (ODh).
The length byte counts itself, the name length and the carriage return terminator. The
name portion must be a valid MegaBasic identifier. PROLOGUES and EPILOGUES do not
need names, but you should define a null name for them, consisting of the two bytes:
2,0Dh. Avoid names matching MegaBasic reserved words, because they would never be
accessible from your MegaBasic program, and no diagnostic is provided.
10-28 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
10
Argument
Word Description
Addressing Accessed Addresses
SS:BP+6 WORD3 Fourth argument
SS:BP+4 WORD2 Third argument
SS:BP+2 WORD1 Second argument
SS:BP+0 WORD0 First argument
This method is designed for fast indexed access to any argument, for supporting omitted
arguments, and to accommodate new argument types. Each argument description
depends on the argument type, i.e., an integer value has one description, a string
variable has another. The table below summarizes the layouts of each argument
description:
In the preceding table, the words ANYVAL and ANYVBL are replaced by the code for the
actual data passed through that argument (i.e., ANYVAL is replaced by INTVAL,
REALVAL or STRVAL, and ANYVBL is replaced by INTVBL, REALVBL, STRVBL or
ARRVBL). In the following pages, we will discuss each argument type in detail, including
both the argument description and how it is specified.
Integer Values
An integer value argument is defined in the subroutine header with the label INTVAL.
The calling program must supply a numeric expression that evaluates to a number that
is an integer or one that can be converted to an integer. This conversion is done
automatically by MegaBasic as needed. The internal description of an integer value
argument consists of three 16-bit words:
The Segment/Offset pair is a double-word pointer to the actual 32-bit integer value.
Arguments of this type can be accessed by the subroutine but changes to this value are
not passed back to the MegaBasic program through this argument. This is generally the
fastest method to pass a numeric value to an assembler procedure.
10-30 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
10
Real Values
A real value argument is defined in the subroutine header with the label REALVAL. The
calling program must supply a numeric expression that evaluates to any number (real or
integer). MegaBasic automatically converts integers to real whenever they are specified
for this argument type. The internal description of an real value argument consists of
three 16-bit words:
The Segment/Offset pair is a double-word pointer to the leading byte (i.e., the lowest
memory offset) of the actual real value. Arguments of this type can be accessed by the
subroutine but changes to this value are not passed back to the MegaBasic program
through this argument.
Subroutines that access real variables are expected to know the format of real numbers.
MegaBasic supports BCD real formats in 8,10,12,14,16, and 18 digit precisions, and
binary real numbers in Intel IEEE double-precision format (8 bytes long), suitable for
8087 or 80287 operation. Any individual copy of MegaBasic supports just one of these
formats.
The Segment/Offset pair is a double-word pointer to the leading byte (i.e., the lowest
memory offset) of the actual real variable passed. Changing the memory contents at this
location will affect the contents of the MegaBasic real variable in the calling program.
Subroutines that access real variables are expected to know the format of real numbers.
String Values
A string value argument is defined in the subroutine header with the label STRVAL. The
calling program must supply a string expression for this argument type. The internal
description of a string value argument consists of four 16-bit words:
10-32 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
10
WORD0 Constant ARRAYI for integer arrays, ARRAYR for real arrays or
ARRAYS for string arrays.
WORD1 Offset of the first byte of the first array element.
WORD2 Segment of the arrayvariable.
WORD3 Number of elements in the array.
WORD4 Number of bytes per element.
WORD5 Offset of array dimension list.
The array element size depends on the array type: integers and reals are identical to
scalar values, and string elements have a length defined in the array definition (a topic
covered below). Large MegaBasic arrays (i.e., those that exceed 64k bytes) cannot be
accessed by assembler procedures because of the complex segmented data structures
involved. The layout of MegaBasic arrays is described below:
The dimension count is the number of elements in that dimension. For example the array
X(99,40) has 100 elements in the first dimension and 41 elements in the second dimension.
The list of dimensions is followed by a word of zeros (16-bit) as a terminator. Immediately
after the zero terminator follows the sequence of array elements. The elements are ordered
such that as you advance sequentially through memory, the right-most subscript varies the
most rapidly and the left-most subscript varies the least rapidly.
The elements of a string array contain both the string and its length in the following data
structure. The first two bytes of each element form a 16-bit word count of the number of
characters in the string. This count is immediately followed by the string (i.e., the
number of characters indicated). The maximum length that the string array element can
hold is the number of bytes per element minus 2. All elements are the same size. You can
modify both the string contents and the length word, but be sure that you do not modify
any bytes past its maximum capacity, nor set the length to any value larger than the
element size minus 2.
in the argument description is set to reflect the actual argument type evaluated (i.e., it is
not marked ANYVAL). For example, if the ANYVAL argument expression turned out to be
an integer, then an integer value description would be provided and its leading WORD
would contain the label INTVAL.
To use such generic arguments, your assembler code must check the first WORD of the
argument description to find out which argument type was passed. Generic arguments
are useful in subroutines that have to operate on any kind of data or in subroutines that
determine omitted arguments by data type context. More steps are required to use such
arguments, so you should really need them before you decide to use them in your
subroutines.
Subroutine Code
The subroutine code accesses the arguments, if any, passed by the calling program, and
executes its designated operation in assembler. MegaBasic always enters the subroutine
at its defined entry point with the following registers setup:
h DS, ES and CS all point to code segment of the assembler package.
h SS:SP points to the FAR return address on the machine stack.
h CX contains the count of how many arguments were actually specified in the
calling MegaBasic program reference. This count includes omitted arguments
only if they were preceded or followed by a comma (i.e., CX contains the
number of comma-separators plus one). CX is zero if no arguments were
specified.
h SS:BP points to the WORD containing the offset of the first (or left-most)
argument. The next WORD up (i.e., at SS:BP+2) points to the WORD with the
offset of the second argument, and so on. An omitted argument is indicated by a
WORD that contains zero.
10-34 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
10
h SS:BX points to the base location on the MegaBasic logical control stack at which
function results are placed. This pointer is meaningless for procedures.
The subroutine is expected to know how to access each of the arguments passed to it
from MegaBasic. This is reasonable since the subroutine definition has defined the
argument types, SS:BP points to CX words that point to the argument data structures
(also in the stack segment), and each data structure begins with the argument type
constant for programming convenience.
It is important that the subroutine code does not rely on any fixed code segment
addresses. MegaBasic loads the assembler package into memory wherever it is possible,
which may not be in the same place at different times. Also, the memory segment it
resides in may move from time to time to make room for new data structures required or
created by the MegaBasic user program. Hence the code must remain segment
relocatable at all times. This also means that your assembler subroutines cannot be
CALLed from external processes or hooked into interrupt vectors, because the assembler
package code segment can and will change during interpreter operation (but not while
your assembler code is executing).
Data tables and other structures may be included in the assembler package and accessed
off of DS, ES or CS (as passed by MegaBasic). NEAR subroutines may also be included in
the package for use by any of the formally declared subroutines.
The CPU stack (i.e., SS:SP) has sufficient space for reasonable use by your subroutines.
However, if you need more than about 100 bytes of stack space (i.e., 50 PUSHes), you
should define your own local stack and switch to it at the appropriate time. Be sure that
you restore the original stack before your subroutine returns.
Assembling a Package
To simplify the understanding, construction and assembly of MegaBasic assembler
packages, there are four files the come with the MegaBasic release disk that will greatly
assist you. These files are:
The assembler files provided were prepared using the MASM 5.x assembler from
MicroSOFT Corporation. ASMPKG produces binary files with the file extension of .BIN,
which you must specify in file names supplied to INCLUDE, ACCESS and DISMISS
statements. You could rename such files with a .PGM extension which MegaBasic adds
by default, but this would probably create confusion with the MegaBasic interpreter files
in the same directory.
You should study the EXAMPLE.asm source file to help understand the material
presented here. Although the volume of information we have discussed may seem
complex, once you see how it is done and begin to write some of your own assembler
packages, you will find it to be about as simple as programming in assembler could ever
be. We highly recommend that, at least for your first few packages, you build packages
by making a copy of the EXAMPLE.asm source file and then modify that copy to suit
your needs (deleting those portions that you do not require). It is always easier to
modify an existing, correct, running program than to build one from scratch.
10-36 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
Appendix A Error Messages
section level 1 1level 1 1
section
A figure bifigure_ap
level 1 level 1
table_bigtable_ap
level 1 level 1
figure_ap level 1
table_ap level 1
This appendix describes all error types and messages reported by MegaBasic. Errors are
reported with a descriptive message, a non-zero error code and the location in the program
where the error occurred. Errors with codes less than 255 can be trapped by the program
and handled by user-prescribed actions. Errors with a 255 error code cannot be trapped
and constitute fatal errors. It is not possible or feasible to recover from errors of this type
and error traps have no effect if set. They are usually revealed during the debugging
phase of program development and do not occur in well tested final versions of
programs.
The ERRSEI statement (Chapter 6, Section 4) is used to set traps for errors that later
occur. When trapped, MegaBasic branches to a user-specified program location and
provides information about the error in the following functions:
When an untrapped error occurs (fatal or otherwise), MegaBasic reports the error
message and its program location on the console screen and terminates the program.
The text of the program line itself is placed in the edit buffer, so that you can
immediately use editing control characters to examine and modify the offending line
after the error is reported. MegaBasic also puts you into the workspace containing the
package where the error occurred so that you can immediately examine the problem.
To assist the program development and debugging process, MegaBasic does not trap
type 10 errors when programs are RUN from the MegaBasic command level. Type 10
errors are those involving errors in program formation, syntax, loop construction, etc.
Such errors need to be exposed during program testing and not hidden by the error
processing mechanisms, as they would be if they were trappable errors. Such errors are
always trapped when the program is run from the operating system command level (under
either run or development versions).
Certain trappable error types have the potential for recovery by retrying the same
operation after waiting some amount of time and/or physically adjusting computer
system components. A good example of this is getting a Not Ready Error when the printer
GFK-0256 A-1
A
paper runs out. Retries can be controlled using the RETRY statement (Chapter 6, Section
4). This mechanism only applies to those trappable errors below which are marked with
an asterisk (*). Read the discussion on the RETRY statement for further details.
A-2 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
A
A-4 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
A
A-6 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
A
A-8 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
A
A-10 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
A
A-12 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
Appendix B Other Operating Systems
section level 1 1
B figure_ap level 1
table_ap level 1
MegaBasic runs on a variety of different operating systems. Each operating system has
its own peculiarities which are difficult, if not, impossible to reconcile with one another.
Hence there are some slight differences in certain capabilities and operational rules
between versions of MegaBasic that run under different operating systems. All such
differences are related to operating system services and do not affect the underlying
language constructs.
The host operating system type can be determined from within your program by
interrogating the the PARAM(5) function (Chapter 6, Section 2), which returns an
integer code corresponding to each operating system supported by MegaBasic.
Operating system dependent routines in your program can use this code at any time to
select the implementation appropriate to the operating system at hand. In this way, you
can develop generic versions of your programs that will execute properly regardless of
the system you run them under.
As the MS-DOS operating system is the most prevalent system in current use, the
MegaBasic Reference Manual describes MegaBasic from the MS-DOS point of view.
Hence Appendix B describes how MegaBasic under other operating systems differs from
the MS-DOS implementation. Appendix B is organized in the following way:
Section Description
Appendix B Xenix386/486 System V
Section 1
Appendix B CP/M-86on 8088/8086 machines
Section 2
Appendix B ConcurrentCP/M-86andMP/M-86
Section 3
Appendix B TurboDos-86
Section 4
GFK-0256 B-1
B
MegaBasic under Xenix 386 System V is very close to MegaBasic under MS-DOS, as
MS-DOS itself was designed with Xenix compatibility in mind. We will describe in this
section how MegaBasic under Xenix differs from MegaBasic under MS-DOS. Because
MegaBasic is a living, growing language, some of the differences described below may
disappear in future releases of MegaBasic.
B-2 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
B
h By modifying the system command that passes the prnxxx file to the spooler,
you may be able to redirect the output to devices other than the standard
printer. The PRINT END <string> statement can be used to specify such
modified printer commands. The <string> is a string expression that evaluates
to the desired command prefix, to which MegaBasic appends the file name
currently in effect. For example the string Ipr -c specifies the default command as
described above; the string more causes the file to be displayed on the console
screen. Note that PRINT END terminates the current printer session and begins
a new one, and that a command specified by the optional <string> applies to
the new printer session rather than the terminated one.
DOS Statements
CP/Mhas no command shell processor, hence you cannot execute operating system
commands from your MegaBasic program. Furthermore, the background processing
supported under PC-DOS is unavailable under CP/M.
B-4 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
B
B-6 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
B
Section 4: TurboDos-86
TurboDos-86 is almost identical to MP/M-86, and this appendix explains only the
differences between those two operating systems. See Appendix B, Section 4 for all
features identical in MegaBasic under both systems.
TurboDos routes all printer characters through the FIFO file assigned to the current user
or the printer. This makes the printer always (logically) available, obviating the need for
printer locking mechanisms. This does however mean that there may be a delay
(possibly significant) between the time that printed material is sent and the start of its
physical printout. PRINT END does nothing if it appears in any MegaBasic programs.
RETRY Procedures
TurboDos does not allow user programs to control all the errors that can occur from
certain file operations. In such cases, TurboDos takes control away from the user
program rather than informing the user program so that it can take appropriate action.
The only retriable errors supported by TurboDos are the locked access errors that can
occur when more than one process is attempting to access the same region of a file
OPENed in SHARED mode or OPEN files which are already OPEN in exclusive
(non-SHARED) mode by another process. Several important errors, however, are not
recoverable. These are listed below:
h Drive select errors cause an abort back to the operating system level like under
CP/M-86, rather than being trapped by MegaBasic as in MP/M-86.
h Bad sector errors can be retried using the TurboDos error handler, rather than
the MegaBasic RETRY capability.
h Read-Only errors cause an abort back to the operating system level.
C figure_ap level 1
table_ap level 1
This section concerns itself with a number of programs external to MegaBasic that
perform functions useful to the development process.
Section Description
GFK-0256 C-1
C
PGMLINK binds the currently running image of MegaBasic with a series of MegaBasic
packages into a single, stand-alone, execute-only file. Such a program may then be run
without having any copies of MegaBasic on the disk, by merely typing its name at the
command level of the operating system, like the other utility programs that came with
your machine. In fact, such programs are indistinguishable from those produced by
compilers and assemblers, as far as their surface appearance is concerned.
To run this utility, you must be prepared to enter the file names of the main program and
the packages which it accesses. You must also be running PGMLINK under the same
version of MegaBasic that you want executing your final program. PGMLINK will display
the floating point precision and operating system type of the version of MegaBasic you
are running under before you enter the names of your program files, so that you can
abort the process if necessary.
PGMLINK first requests the name of the file on which your complete program system
will be built. If it already exists, then its contents will be automatically erased before
proceeding. PGMLINK then requests the file name of the main program and the names of
the subsequent support packages. When all names have been entered, type a carriage
return to the file name request to terminate the program. Your stand-alone program will
then be ready to run.
Your program will run the same way as it would as if you executed it under the RUN
version. It can still ACCESS or INCLUDE other program files that are on the disk if
needed. All packages that you bound together in the executable file are brought into
memory at the same time, and hence there must be sufficient memory available to load
all of them and execute the entire program. If any packages contained in this file are
DISMISSed and removed from memory, they must reside on another separate disk file
if they are ever needed again by a subsequent INCLUDE or ACCESS statement. No
program component packages can be individually extracted from the execute-only file
once they have been bound together.
There is one minor difference in the way execute-only programs behave that you should
be aware of. The command tail that the operating system communicated to any transient
command (like BASIC, a text editor, RUN, etc.) consists of all characters typed
immediately after the command name. If you use command tails, you know that the first
word in every command tail is the name of your program, because it is typed
immediately after the command BASIC or RUN. However, your execute-only program
file name will be the command and hence it will not appear as the first word in the
command tail, or anywhere else. There will always be one less word in command tails
received by execute-only programs as compared with the same programs run under
BASIC or RUN explicitly. This incompatibility is important if you pass arguments to your
program in the command tail.
Under MS-DOS, console input may come from a text file instead of from the keyboard.
This capability is quite useful when you are creating stand-alone programs with the
PGMLINK facility. By preparing a text file using your favorite editor, you can store the
names of the program files required for any PGMLINK process, so that you no longer
have to type them in or even remember what they are. This is especially useful when
your stand- alone program consists of many packages. Once your text file is prepared,
type the following command at the operating system level:
RUN PGMLINK < pgmnames.txt
C-2 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
C
where pgmnames.txt is any file containing the necessary program file names. The left
arrow (<) causes MegaBasic to take its standard console input from the specified file
instead of the actual console keyboard.
The text file containing the list of program file names must be constructed in an identical
manner to the way you would type the names from the keyboard in response to
PGMLINK file name requests. This format is summarized as
Another point about PGMLINKed systems should also be mentioned. When your
stand-alone system begins execution, all the supporting packages will be in an
uninitialized state: equivalent to free workspaces (see the SHOW command). If you
perform any processing which would normally release free workspaces, your packages
will be removed from memory before you may have had a chance to INCLUDE or
ACCESS them.
DOS commands, in particular, will always get rid of all free packages in order to provide
the greatest possible working memory to the subsequent DOS command. This can be
avoided by making sure all of your supporting packages are INCLUDEd or ACCESSed
before giving the DOS command.
A surprising amount of memory space is taken up by blanks inserted into the code and
REMarks that have nothing to do with program execution. Well commented, structured
BASIC programs typically have 30% or more of their memory area invested in blanks
and remarks. The CRUNCH program conveniently optimizes a BASIC program by
creating a new BASIC program without spaces and remarks, leaving the execution
properties unchanged. This utility is useful only when more memory or source code
protection is desired.
Both files may be typed with or without the .PGM program file name extension, and
.PGM is assumed when omitted. CRUNCH aborts if the original program file cannot be
found in the directory or if the original program and result files are the same. CRUNCH
tells you if the result file exists or not, and requests confirmation before proceeding.
CRUNCH creates a new file automatically as needed for the resulting CRUNCHed program.
C-4 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
C
You can specify the same file names for the original and the result files only when they
are on different drives. CRUNCH will automatically derive such a result file name from
the original if you specify the result file as a drive specifier only. For example, the two
CRUNCH commands below both mean the same thing:
CRUNCH A:MYPROG B:
CRUNCH A:MYPROG B:MYPROG
The <options> are of the form: letter=value (with no spaces). Multiple options are
separated with spaces. Omit the <options> to display settings. The option letters are
defined as follows:
When you are satisfied with your choices, you can then have all MegaBasics
immediately configured at once, or you can have them configured one by one,
individually subject to your confirmation.
C-6 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
C
Be sure to only modify copies of your original software while protecting your originals by
keeping them safely away from your computer system. Each of the currently available
options is described below.
When you set the video mode byte, specify the sum of all options you wish to select. For
example, the standard setting is 7 (1+2+4). This byte affects only the MS-DOS versions of
MegaBasic.
C-8 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
C
Lower-Case Symbols
You can configure MegaBasic to leave the letter case in names the way you type them
instead of forcing them to upper case. In this mode, MegaBasic will show the case spelling
you typed for the first reference to the name, no matter how you spell it in subsequent
references. This lets you type a name in any case you like, but it will always LIST the
same way throughout the program. You can change the case spelling of existing names
using the NAME command (which affects all references). Shared names in other packages
do not have to match the letter case of references to them, i.e., shared name linkage is
case-insensitive.
This feature is for purely esthetic and programming style purposes. It will not affect
program size or performance, but it can enable you to make your software look a little
more the way you would like it to. MegaBasic versions earlier than 5.72 use
case-sensitive name linkage, so avoid using this feature for programs to be executed
under such earlier versions.
C-10 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
C
Interrupt Trapping
Once FLIP is installed, it monitors a number of interrupt vectors that are never called,
except when the system crashes due to a programming error. For example, INT 6 is
called when the CPU attempts to execute an invalid instruction. Normally, an INT 6 will
crash the machine because neither DOS nor the BIOS ever bothered to trap this
important interrupt vector and do something intelligent with it. FLIP on the other hand,
traps INT 6, displays the CPU registers and stack contents and then terminates the
offending program. The interrupts FLIP traps in this manner are: 0 (divide error), 1
(single-step), 3 (breakpoint), 4 (numeric overflow), 6 (invalid instruction), and 7
(coprocessor unavailable) and 13 (protection violation). Programs that have valid uses
for these interrupt vectors would normally insert their own interrupt vectors, overriding
the ones set by FLIP. The protection FLIP provides is present for all programs run, not
just MegaBasic, and is included with FLIP only because of a design omission in DOS
itself. FLIP consumes only a couple kilobytes of memory and is therefore economical to
have installed all the time.
Note
Aborted application will not have had a change to perform any
necessary termination clean-up, which can leave the system in an
unstable state.
C-12 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
C
EVENTS is a removable TSR that lets MegaBasic applications respond to a wide variety of
simultaneous external EVENTS on MS-DOS systems without using wasteful status polling
mechanisms. This is accomplished by using the logical interrupt facilities of MegaBasic to
catch the events as they happen and queue them for processing by the MegaBasic
application. The events currently supported are:
h Mouse events (buttons up/down/click, moved/stopped, etc.)
h Up to 10 independent timers
h Up to 40 hot keys
h AnyShift/Ctrl/Alt/Caps/Insertkey combinations
h Ctrl-Break
Serial port events are not provided by EVENTS, because the MegaCOMM.sys driver
(available separately) is already designed to provide logical interrupt support for all
aspects of interrupt driven serial communication (e.g., buffer getting full, buffer empty,
special character received, error conditions, line control, baud rates, etc.).
The basic operation of EVENTS is very simple. After EVENTS.COM is installed as a TSR,
you (i.e., the application) tell EVENTS to signal the running MegaBasic application when
some specific event occurs and to identify this event with an event id (a 16-bit integer of
your choice). Then the application continues about its business without any more
attention on the pending event. When it occurs, the application automatically suspends
its whatever it was doing, processes the event in the manner prescribed for its event id,
and resumes the application where it left off (and without any additional polling or
other programming at the application level).
Installing EVENTS.COM
EVENTS must be installed by running it prior to executing MegaBasic and any applications
that use it. Once installed, EVENTS provides a set of calls you can make through
INTerrupt OBFh that make requests for events to be triggered. Its various functions
are selected by setting AH to a function number along with any other register values
appropriate to that function. All functions are described later in this document.
You can remove the EVENTSTSR from memory by running it again with the/U
command line option. EVENTS will not be able to remove itself from memory if another
TSR has hooked the interrupt vectors used by EVENTS, and will display a message in
such a case. If the uninstall is successful, the original interrupt vectors that were hooked
by EVENTS will be restored and all memory used by EVENTS will be released back to
DOS (only about 2300 bytes). EVENTS currently hooks following software interrupt
vectors:
h Interrupt 09H for all keyboard shift key and hot key events
h Interrupt lBh for Ctrl-Break events
h Interrupt lCH for the timer events
h Interrupt 17h for printer events
Once installed, EVENTS provides a variety of functions to enable and disable the various
events supported. These functions are called through INTerrupt OBFh, with a function
number in register AH and additional parameters in other registers as needed. Since the
event facilities are extended from time to time, the function details are documented in
EVENTS.DOC, along with the error codes, recent changes and other information.
Timer Facilities
EVENTS supports 10 timers driven by a single logical interrupt. Times are measured in
milliseconds (resolved to IBM-PC timer-ticks of about 54 msecs). You can specify a 32 bit
unsigned value for the number of milliseconds on a timer resolved up to the next tick
(up to a maximum of about 3 weeks). When the timer times out, a MegaBasic logical
interrupt is invoked, and the timer stops counting. Several functions are provided to: set
timer values, set timer logical interrupts, pause/resume a timer, get timer status.
Mouse Events
The mouse is a rich source of external events which can really consume a lot of processing
power using a polled mechanism, but can be very efficiently handled using the
event-driven approach provided by logical interrupts. EVENTS assumes a standard
mouse driver is already installed supporting an INT 33h entry point into the mouse
functions. One of the EVENTS functions enables and disables the mouse events.
Enabling the mouse events tells the physical mouse driver to inform the EVENTSTSR
about any change occurring on the mouse. You should disable the mouse events after
your application has finished with using the mouse to clear the connection between the
EVENTS and the Mouse driver.
C-14 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
C
Left and right mouse-button events are independently set for each button and can be
any combination of: pressed, released, changed, clicked and double-clicked. A change
event is the press or release of a button and is posted immediately; a press or release event
is only posted if not part of a click; a click event is only reported when not part of a double
click. Therefore, except for a change event, mouse button events are not reported until the
button has remained unchanged for about 90 milliseconds.
The mouse-position events currently supported are mouse-moved and mouse-stopped;
movement is posted immediately; stoppage is reported only after movement has ceased
for at least 90 milliseconds. You can also define a region on the screen where the mouse
must be in order for the event to be triggered.
All mouse events return a pointer (i.e., ES:BX returned to the logical interrupt handler)
to a data structure containing 4 words:
Once an event occurs, you must re-arm the trap to activate the event again. Click-events
take precedence over pressed/released events. You can call the various mouse driver
functions directly (i.e., through Int 33h) for additional information about the mouse or to
control it in other ways.
The program files described below are provided with the MegaBasic software system.
They can be ACCESSed from your application and freely used and/or given away in any
manner you wish.
LlBRARY.pgm
The LIBRARY.PGM contains a variety of procedures and functions that you may find
useful in the development of your own programs. These modules also demonstrate the
use of many features and serve as additional examples to supplement the manual. At the
present time, all documentation for these routines is limited to the remarks in the
LIBRARYPGM program file.
PCBASLlB.pgm
The PCBASLIB.PGM contains a variety of procedures and functions that you may find
useful in the development of programs intended to run on the IBM-PC family of
microcomputers. Many subroutines have been added to make it easy to access the
various capabilities of the screen and keyboard, in particular. At the present time, all
documentation for these routines is limited to the remarks in the PCBASLIB.PGM
program file.
C-16 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
C
This section covers the MegaBasic products currently available. For additional
information, pricing and ordering, and additional support, please contact your
MegaBasic representative, the MegaBasic Bulletin Board System (415-459-0896) or
MegaBasic Language Products at PO. Box 723, Fairfax CA 94978.
The MegaBasic compiler is also useful for completely analyzing and verifying the
syntactic correctness of existing MegaBasic programs, often finding errors in code you
may have thought correct.
Compilations are command-line oriented, with parameters specified in the command
line, in response files, or both. It compiles at a rate exceeding 1000 statements per second,
even on s-l-o-w computers. It reports all source code errors and supports all MegaBasic
constructs.
The compiler supports various compile-time options to control optimization, line
number inclusion, symbolic map file generation, error messages, package dependencies,
output file format, destination path for result files, etc. Complex multiple package
applications may be compiled through a response file, so that you only have to specify
its compilation details once. The components of this product include:
h A compiler with libraries for 8 and 14 digit BCD and 16 digit 80x87 IEEE binary
floating point. Other libraries supporting protected mode operation giving
access to extended memory come with the Extended MegaBasic product.
h A utility automating the compilation of large applications.
h A utility combining multiple packages into a single .EXE file.
h A utility generating extensive cross-reference listings for documenting large,
multiple package applications.
h A compiler manual supplementing the MegaBasic Language Reference and
Programmer ’s Guide.
The MegaBasic compiler is used in conjunction with the MegaBasic Development
System running under MS-DOS or compatible operating systems (e.g., Concurrent DOS).
Extended MegaBasic
Extended MegaBasic runs completely in protected-mode in order to provide full access to
as much as 16 megabytes of extended memory, otherwise it is indistinguishable from
standard MegaBasic.
A DOS-extender is not needed with this special version of MegaBasic. Extended
MegaBasic is a DOS-extender, dedicated to developing and running very large
MegaBasic applications with lots of data. It is the first, and possibly the only, BASIC that
runs in protected-mode on MS-DOS. It provides the following features:
h It executes MegaBasic programs under MS-DOS with access to up to 16
megabytes of memory, providing an ideal environment to develop extremely
large applications in BASIC without paying workstation or mainframe prices.
h String and numeric arrays may be up to 16 megabytes. The total number of
arrays and strings is limited only by the amount of available memory.
h 100k more memory is provided in the 640k base memory region for DOS shell
commands executed under extended MegaBasic than under standard
MegaBasic. This is because all but 8k of code and 16-128k of its data resides in
extended memory.
h Nearly all existing MegaBasic programs execute perfectly without any
modifications. Some programs that involve themselves in direct machine access
(using FILL, EXAM and CALL statements) may require some very minor
changes.
C-18 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
C
System Requirements
Extended MegaBasic requires a certain minimum hardware configuration to operate.
This minimum is generally satisfied by PC-AT or PS-2 class machines using 80286, 80386
or 80486 microprocessors. Specifically, the requirements are:
h An 80286, 80386 or 80486 microprocessor. Systems that routinely process over
1000 interrupts per second may require an 80386/80486, due to the limitations of
the 80286 microprocessor.
h At least 128 kbytes of available memory in both the base megabyte and the
extended memory regions. Expanded memory is neither used nor affected.
h DOS version 3.0 to 5.x running in real-mode, or in the Virtual-8086 mode of the
80386/486 if VCPI/DPMI is supported (as provided by most 386 memory
managers such as QEMM, 386MAX and Microsoft Windows).
h A standard IBM-compatible ROM BIOS that includes support for extended
memory. Any system that supports extended memory VDISK drivers will
support protected mode Extended MegaBasic.
h Assembler packages are supported under Extended MegaBasic, but there some
“rules of the road” that must be obeyed by the assembler code when running in
protected mode.
The Extended MegaBasic System includes both the development and runtime systems,
support for both decimal BCD and IEEE 80x87 binary floating point, extended memory
versions of the compiler run-time libraries (requires MegaBasic compiler), plus all
utilities, full documentation and no-nonsense unlimited license to bundle the run-time
system with your applications distributed to third-parties.
h Windows are view-ports through which you view all or part of an arbitrarily
large virtual screen behind it. The virtual screen portion seen through the
window can be instantly repositioned or resized.
h Window style and layout includes border type, shadow and color, foreground
and background colors for output and for input fields, window size, placement
and visibility mode, virtual screen size, and cursor size and visibility.
h The number of active windows is unlimited and you can control how windows
overlap. Deleting a window releases all its memory for use elsewhere in your
program.
h Windows can appear and disappear instantly or gradually (explode, implode,
digital fade, etc.). They can be moved about the screen instantly, or slid across
the screen under program or mouse control.
h Ordinary MegaBasic PRINT statements are used to send output to VSCREEN
windows. Field-input editing and menu selection routines are used for keyboard
input and mouse control.
h ANSI escape sequences to windows are supported. Cursor locations relate to the
virtual screen. Character output and other operations are supported on hidden,
visible or partially overlapped windows.
h VSCREEN includes other packages that provide high-level functions, such as
screen-forms and field management, menu-bars with drop-down menus, file
selection, directory browsing, multiple text-file browsing, and more.
VSCREEN supports monochrome, Hercules, CGA, EGA and VGA under MS-DOS on any
IBM-PC compatible machine. It includes 6 major packages, extensive demos illustrating
all features and capabilities, full source code, 80 page manual and license to include
execute-only components in your MegaBasic applications supplied to third-parties.
C-20 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
C
C-22 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
C
OtherAnsiPLUS Extensions
h Ability to let you scroll back many screen-fulls of lines that have recently scrolled
off the top of the screen out of view.
h AnsiPLUS highlights keys you type as they are displayed on the screen, visually
separating typed entries from computer output.
h Unlike standard drivers, AnsiPLUS displays a cursor for typed entries while in
graphics mode.
h AnsiPLUS provides functions to insert and delete lines and characters.
h AnsiPLUS has a “transparent background mode” that writes each output
character in the current foreground color without changing the background
color.
h You can apply bold, underline, black shadow, and slant enhancements, in any
combination to output characters in 16-color graphics modes.
h AnsiPLUS allows blanks between parameters, commas and semicolons as
separators, single or double quotes around character parameters, and signed
parameters up to 32767.
C-24 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
Appendix D Miscellaneous Information
section level 1 1
D figure_ap level 1
table_ap level 1
This appendix covers various subjects that are less likely to be needed on a regular basis,
but are nonetheless of vital importance at certain times. The following areas are covered:
Appendix D MegaBasic Brief summary of all changes to MegaBasic made since the
Section.1 Enhancements previous MegaBasic manual (April 1989)
GFK-0256 D-1
D
This appendix summarizes the major changes to MegaBasic that have occurred since
MegaBasic Reference Manual, Revision C (April 1989). It provides only very brief
references to each item are given and you should seek the appropriate section in the
manual for complete information. Users familiar with MegaBasic versions earlier than
5.65 can study this appendix for a quick review of recent extensions that may impact
current software development efforts. With a couple minor exceptions, MegaBasic has
been extended in an upward compatible manner to preserve the operating
characteristics and executability of all programs developed under earlier versions of
MegaBasic. Descriptions of minor improvements are excluded.
D-2 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
D
D-4 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
D
The table below lists all the reserved words used by MegaBasic. None of these words are
available for use as names of structure fields, variables functions, procedures or
line-labels.
The two names PROLOGUE and EPILOGUE are not really reserved words, but they do
have special meaning in conjunction with packages and hence you should avoid using
them for other purposes as well. From time to time, new built-in statements and functions
may be added to the repertoire of MegaBasic. User-assigned names in older programs that
match new reserved words must be renamed in order to use newer versions of MegaBasic. To
this end, MegaBasic automatically renames such occurrences when a program is first loaded.
This action, which takes place on a LOAD, INCLUDE, ACCESS, MERGE or LINK (in the
D-6 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
D
development version only, not in RUN), merely doubles the leading character of the name until
it no longer matches any reserved word or user defined name. MegaBasic reports each name
reassignment it makes to you on the console.
Special Characters
In addition to the various reserved words, MegaBasic also uses most of the symbols in
the standard ASCII character set for specific purposes within program statements and
commands. For example, letters (A-Z, a-z), digits (0-9) and underscores (_) are used to
form identifiers; numeric constants are formed from the digits, letters A through F, plus
(+), minus (–) and period (.). A complete summary of all special characters follows
below:
D-8 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
D
The table below lists all 256 byte values in decimal, hexadecimal, binary, and in ASCII.
IBM compatible keyboards provide a number of extended keys, which produce a
two-character sequence when typed. The first is always a null character (ASCII zero)
that indicates a second extended key-code follows. The second code identifies the special
key itself and these codes are shown in the table for your convenience. You can safely
assume that if a null character is input from the console, then an extended code will follow
for which your program should wait. The following IF statement illustrates a simple way
to input a single normal or extended key.
IF (let C$ = inchr$(0)) = chr$(0) then C$ = C$+inchr$(0)
Be sure to disable the MegaBasic Ctrl-C mechanism (by setting PARAM(l) to 1) while
performing one-at-a-time character input, so that none of the input characters are lost to
the Ctrl-C detection mechanism.
Some of the extended codes are typed with the Ctrl or Alt keys held down and are
indicated in the table by a ctl or alt superscript. Others are keys that have special names,
such as Ins or Del, which are shown by name. Still others are called function keys and are
indicated as F1, F2, ..., F10. While some computers may have additional extended keys
beyond those shown, we have included only those that are in general use and available
on the vast majority of IBM compatible keyboards.
The ASCII codes from 128 to 255 are called the extended ASCII codes (unrelated to the
extended keys described above). Although they cannot normally be typed at the keyboard,
more recent keyboard drivers often let you type any arbitrary ASCII code by typing its
code in decimal digits while holding the ALT-key down. For example, ALT-234 generates
an ASCII 234 code as soon as you release the ALT key. Be careful that you do not type
extended ASCII characters into a program, except for characters inside quotes (i.e.,
within string literals), because they will conflict with the internal binary representation
of the program line.
Dec Hex Binary Key Ext Dec Hex Binary Key Ext
0 00h 0000000 @ ctl 32 20h 00100000 sp
1 01h 00000001 Actl 33 21h 00100001 !
2 02h 00000010 Bctl 34 22h 00100010 *
3 03h 00000011 Cctl 35 23h 00100011 #
4 04h 00000100 Dctl 36 24h 00100100 $
5 05h 00000101 Ectl 37 25h 00100101 %
6 06h 00000110 Fctl 38 26h 00100110 &
7 07h 00000111 Gctl 39 27h 00100111 ’
8 08h 00001000 bksp 40 28h 00101000 (
9 09h 00001001 tab 41 29h 00101001 0
10 0Ah 00001010 1feed 42 2Ah 00101010 *
11 0Bh 00001011 Kctl 43 2Bh 00101011 +
12 0Ch 00001100 Lctl 44 2Ch 00101100 ,
13 0Dh 00001101 ret 45 2Dh 00101101 –
14 0Eh 00001110 Nctl 46 2Eh 00101110 .
15 0Fh 00001111 Octl 47 2Fh 00101111 /
16 l0h 00010000 Pctl 48 30h 00110000 0
17 11h 00010001 Qctl 49 31h 00110001 1
18 12h 00010010 Rctl 50 32h 00110010 2
19 13h 00010011 SctI 51 33h 00110011 3
20 14h 00010100 Tctl 52 34h 00110100 4
21 15h 00010101 Uctl 53 35h 00110101 5
22 16h 00010110 Vctl 54 36h 00110110 6
23 17h 00010111 Wctl 55 37h 00110111 7
24 18h 00011000 Xctl 56 38h 00111000 8
25 l9h 00011001 Yctl 57 39h 00111001 9
26 lAh 00011010 Zctl 58 3Ah 00111010 :
27 1BH 00011011 [ctl 59 3Bh 00111011 ; F1
28 1Ch 00011100 \ctl 60 3Ch 00111100 < F2
29 1Dh 00011101 ]ctl 61 3Dh 00111101 = F3
30 1Eh 00011110 ^ ctl 62 3Eh 00111110 > F4
31 1Fh –––11111 _ ctl 63 3Fh 00111111 ? F5
D-10 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
D
Dec Hex Binary Key Ext Dec Hex Binary Key Ext
64 40h 01000000 @ F6 96 60h 01100000 ’ F3ctl
65 41h 01000001 A F7 97 61h 01100001 a F4ctl
66 42h 01000010 B F8 98 62h 01100010 b F5ctl
67 43h 01000011 C F9 99 63h 01100011 c F6ctl
68 44h 01000100 D F10 100 64h 01100100 d F7ctl
69 45h 01000101 E 101 65h 01100101 e F8ctl
70 46h 01000110 F 102 66h 01100110 f F9ctl
71 47h 01000111 G home 103 67h 01100111 g F10ctl
72 48h 01001000 H ↑ 104 68h 01101000 h Flalt
73 49h 01001001 I PgUp 105 69h 01101001 i F2atl
74 4Ah 01001010 J 106 6Ah 01101010 j F3atl
75 4Bh 01001011 K ← 107 6Bh 01101011 k F4atl
76 4Ch 01001100 L 108 6Ch 01101100 l F5atl
77 4Dh 01001101 M → 109 6Dh 01101101 m F6atl
78 4Eh 01001110 N 110 6Eh 01101110 n F7atl
79 4Fh 01001111 O end 111 6Fh 01101111 o F8atl
80 50h 01010000 P ↓ 112 70h 01110000 p F9alt
81 51h 01010001 Q PgDn 113 71h 01110001 q F10atl
82 52h 01010010 R insert 114 72h 01110010 r
83 53h 01010011 S del 115 73h 01110011 s ←ctl
84 54h 01010100 T 116 74h 01110100 t →ctl
85 55h 01010101 U 117 75h 01110101 u endctl
86 56h 01010110 V 118 76h 01110110 v pgdnctl
87 57h 01010111 W 119 77h 01110111 w homectl
88 58h 01011000 X 120 78h 01111000 x Ialt
89 59h 01011001 Y 121 79h 01111001 y 2alt
90 5Ah 01011010 Z 122 7Ah 01111010 z 3alt
91 5Bh 01011011 [ 123 7Bh 01111011 ( 4alt
92 5Ch 01011100 \ 124 7Ch 01111100 | 5alt
93 5Dh 01011101 ] 125 7Dh 01111101 ] 6alt
94 5Eh 01011110 ^ F1ctl 126 7Eh 01111110 ~ 7alt
95 5Fh 01011111 _ F2ctl 127 7Fh 01111111 “ 8alt
Dec Hex Binary Key Ext Dec Hex Binary Key Ext
128 80h 10000000 9alt 160 A0h 10100000
129 81h 10000001 0alt 161 A1 h 10100001
130 82h 10000010 162 A2h 10100010
131 83h 10000011 163 A3h 10100011
132 84h 10000100 pgupalt 164 A4h 10100100
133 85h 10000101 165 A5h 10100101
134 86h 10000110 166 A6h 10100110
135 87h 10000111 167 A7h 10100111
136 88h 10001000 168 A8h 10101000
137 89h 10001001 169 A9h 10101001
138 8Ah 10001010 170 AAh 10101010
139 8Bh 10001011 171 ABh 10101011
140 8Ch 10001100 172 ACh 10101100
141 8Dh 10001101 ↑ctl 173 ADh 10101101
142 8Eh 10001110 174 AEh 10101110
143 8Fh 10001111 175 AFh 10101111
144 90h 10010000 176 B0h 10110000
145 91h 10010001 ↓ctl 177 B1h 10110001
146 92h 10010010 insctl 178 B2h 10110010
147 93h 10010011 delctl 179 B3h 10110011
148 94h 10010100 180 B4h 10110100
149 95h 10010101 181 B5h 10110101
150 96h 10010110 182 B6h 10110110
151 97h 10010111 183 B7h 10110111
152 98h 10011000 184 B8h 10111000
153 99h 10011001 185 B9h 10111001
154 9Ah 10011010 186 BAh 10111010
155 9Bh 10011011 187 BBh 10111011
156 9Ch 10011100 188 BCh 10111100
157 9Dh 10011101 189 BDh 10111101
158 9Eh 10011110 190 BEh 10111110
159 9Fh 10011111 191 BFh 10111111
D-12 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
D
The integer version of MegaBasic is capable of running all programs that ran under
earlier versions of MegaBasic which did not support integers. It is 100% compatible with
such earlier programs and you do not have to make any changes. Since integers can be
read from and written to files, the READ and WRITE statements of earlier programs are
automatically altered when initially loaded to ensure that only floating point values are
written to the file. This feature is more fully discussed later in these application notes.
Integers provide substantial gains when they replace floating point arithmetic, array
subscripts and string indexing. To achieve 100% compatibility, MegaBasic assumes all
numeric variables are to be floating point unless you specifically state otherwise. Hence
your program cannot take advantage of the performance improvements provided by
MegaBasic integers until you change your program. This is actually a very easy process
for most programs and is accomplished by following the steps described below:
h Thoroughly read and understand all information described for this process.
h After bringing up MegaBasic, load the program to be converted to integer
operation into your workspace. MegaBasic will immediately scan this program
(if it was created under a non-integer MegaBasic) and automatically insert the
reserved word REAL after each READ and WRITE throughout your program.
This is done to ensure that floating point numeric file transfers do not suddenly
become integer transfers, with erroneous results.
h In virtually all programs, the overwhelming majority of numeric variables and
functions are used to store and return integer values. With this in mind, place
the following DEF statement at the top of your program to set all (numeric)
variables and functions to an integer type:
DEF INTEGER “A-Z”
h Carefully examine your program to determine if it uses any variables or
functions in a floating point capacity. If it does use floating point, make a list of
the names of all such variables and functions throughout your program. With
this done, insert the following statement after DEF statement described above:
DEF REAL name1, name2, name3, name4, ...
where the list of names is the list of real variables found in your program. Be
sure to specify empty parentheses ( ) on names of arrays. Additional DEF
statements may be used if your list is longer than one program line. Do not
include any floating point function names in these DEF statements. Instead,
insert the word REAL into each formal FUNCtion definition line immediately
before the FUNC reserved word:
DEF REAL FUNC RTOTAL(LIST_VBL).
h Go through your program looking for divide operations (/). For each one found,
examine how it is used and determine if an integer divide (DIV) could be used in
its place. A regular divide is an exclusively floating point operation which is slow
in floating point and slower dividing an integer by an integer (because of the
extra conversions). The DIV operation with integers is from 5 to 8 times faster.
h Remove the reserved word REAL from all READ and WRITE statements which do
not involve floating point numeric data transfers.
D-14 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
D
h Thoroughly test your program and ensure that the results it obtains are identical
with those obtained under the earlier non-integer version of MegaBasic under
which it was originally developed.
This procedure will be effective and straightforward with most programs. As you
become accustomed to integers in your daily programming work with MegaBasic, you
will find your own ways of using them suited to your style of programming. This
conversion procedure is intended only as a bridge to get you started.
Programs developed under prior versions of MegaBasic will likely be named with a .zba
suffix (secondary or type name). Files with other than the .pgm default suffix must be
spelled out in full in order to LOAD them. MegaBasic programs starting with the Version
4.0 series use a totally different internal encoding from programs developed under
earlier versions. When programs using the earlier encoding are LOADed, MegaBasic
Versions 4.0 and later perform a 100% translation of the program into the newer form.
Several enhancements to the language syntax have been made which are incompatible
with pre-version 4.0 programs. However when such programs are LOADed the
automatic translator makes all the appropriate program modifications as needed to be
100% compatible. When developing new source code under Version 4.0 and later, you
will have to use the new syntax because no translation is done after its initial LOAD.
These relatively minor modifications are all described below:
h To allow later extension of PRINT formatting, all static formats must be enclosed
in quotes (“ or ””). Lower case as well as upper case format characters are
accepted.
h Since functions may be arbitrarily named, their DEF statements must contain the
reserved word FUNC, e.g., DEF FUNC TOTAL(X,,Y). The translator leaves FN in
all earlier programs only to ensure uniqueness of identifiers. You can later
rename functions to anything you like.
h Since names must be unique, the earlier rule that arrays and scalars can be given
the same name is no longer applicable. To ensure uniqueness of array names
when translating earlier programs, MegaBasic doubles the first character of
every array reference in the program. You can change their names after this to
suit your own special needs. For example A(I) becomes AA(I), X3a) become
XX3a), and so on.
h String parameters in DEF statements are passed as localized values just like
numeric parameters (i.e., as local variables). To support the earlier method of
simple string parameter copying which is not re-entrant, each parameter is
preceded by a percent sign (%). This is only done in the function DEF statement
and never in references to functions. The only reason for using this method for
passing strings is to be compatible with programs that rely on the side-effects of
that approach or for the slightly faster execution it provides. For the sake of
completeness and consistency, numeric parameters may be passed in the same
manner by flagging them with percent signs as well.
h FILL and EXAM statements (Chapter 7, Section 3) have been made consistent
with READ and WRITE statements in the way they interpret their data list.
Byte-oriented transfers must be preceded by an ampersand (&); word-oriented
transfers must be preceded by an at-sign (@). If neither of these lead-in
characters is present, then data is transferred just like the other file operations:
numeric floating point and packed-string with a string header.
D-16 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
D
h MEMSET and LINE statements do not exist because they are completely
unnecessary. But in MegaBasic LINE lists erroneously as LOCAL and such
statements must be deleted from the program.
h Your program REMarks may have strange spelling errors due to the keyword
differences in MegaBasic. The quickest and easiest way to fix these is with the
command: EDIT REM which extracts all REMarks for your editing (Chapter 2,
Section 3).
h Do not attempt any of the North Star Personalization procedures on MegaBasic
as they are not the same. Instead, use the CONFIG program described in
Appendix C, Section 3, which implements all personalization options available.
h Since MegaBasic format strings are always string expressions, format
specifications that include a dollar sign ($) may appear to MegaBasic as dynamic
formats, which will subsequently execute incorrectly. Such formats appear to
begin with a string variable (e.g., Z$12F2, C$8I, etc.). You can fix this problem by
surrounding each such format with quotes ( ), for example: %“Z$12F2”,
%“C$8I”, etc.
h FNDEFinitions must appear as the 1st statement on the line that you define
them. North Star BASIC allows it to appear anywhere in the line which results in
much more time spent in the load-up process prior to program execution.
h Through system errors in some North Star BASICs, program lines may
erroneously contain control characters. MegaBasic will display such bad
characters as question marks (?) for your correction.
h Lines, commands, and direct statements may be entered in any combination of
upper and lower case. MegaBasic converts all lower case letters not inside quotes
( ) to upper case before proceeding.
h User defined names with more than one character (e.g., Tl, A$, FNS6, Z3$, etc.)
must be entered run together without any inserted spaces to avoid a syntax
error. The left parentheses following function, string, or array names is
considered part of the name when applying this rule.
h Line-feeds may be entered into the program text for longer lines, and so that
spaces can be inserted between lines to make the text more readable. When
listed, each line-feed expands into a LF-CR sequence.
D-18 MegaBasicLanguage Reference and Programmer’s Guide Reference Manual - September 1994 GFK-0256
Index
Accessibility & program changes, 10-22 Addressing memory, 7-44 , 7-45 , 7-46 ,
9-37
Accessing
Advancing the input cursor, 1-15
accessing structured variables, 5-25
bits in strings, 5-16 Aggregate data structures, 5-18
command tails, 9-28 Alphabetical, error message, A-2
CPU resources, 7-43
current date, 9-34 Alteration command summary, 2-18
current time of day, 9-33 Altering
data files, 7-21 , 7-26 continuable programs, 10-22
DATA statements, 5-6–5-7 file sizes, 7-29
edit buffer contents, 9-28 program line numbering, 2-24
external subroutines, 10-3 programs, 2-18 , 2-20
external subroutines/data, 10-12 sequential execution, 6-1
external variables, 10-4 string length, 4-20 , 5-6 , 5-10
files, 7-4 , 7-43 the OPEN file limit, C-8
files and I/O devices, 9-25 Ambiguous expressions, 4-11 , 4-16
files as devices, 7-3 , 7-15
integers, 5-16 Ampersand lead-in, 7-30 , 7-33
intermediate calculations, 5-11 AND numeric operator, 3-19
memory content, 9-37
AND string operator, 4-11
memory contents, 7-44
name of open file, 9-31 ANSI sequences, C-22
open-ended arguments, 8-8 ANSI.sys console device driver, C-22
packages, 10-12
program constants, 5-6 Appending
programs on files, 2-15 , 2-16 program modules, 2-27
string arrays, 4-7 strings, 4-11
substrings, 4-19 vector variables, 3-28
system resources, 9-33 Argument
text files, 7-26 input to functions, 3-23 , 4-23
GFK-0256 Index-1
Index
Index-2 GFK-0256
Index
record locking mechanisms, 7-38 integers, 5-16 , 7-30 , 7-34 , 7-44 , 9-22
retry of recoverable errors, 7-42 NOT, 4-11
translation from prior versions, D-16 operators, 3-14 , 3-16 , 3-19 , 3-22 , 4-10 ,
workspace removal, 10-22 4-11 , 4-13 , 4-16
OR, 4-11
Automatic retry of recoverable errors,
packages, 10-25
6-23–6-24
program format, 2-15
Available representation, 3-4
functions, 9-1 rotation, 9-22
memory space, 9-37 string operators, 4-10 , 4-13
space remaining on disk, 9-30 string searching, 9-16
Avoiding program duplication, 4-23 , 8-1 , XOR, 4-11
8-13 Bit
Avoiding program duplications, 3-23 access, 5-16
addressing, 5-16
function, 9-22
manipulation, 4-13 , 9-22
B processing, 5-16
rotation, 9-22
B-formats, 7-8
searching, 9-23
B-Tree utilities, C-21 statement, 5-16
Background processes, 7-50 , C-13 string comparisons, 4-17
string logical combinations, 4-13
Background programs, 7-56 strings, 9-22
Backing up the input cursor, 1-15 vector processing, 4-13
Backslash separator, 1-8 Blank
line insertion, 1-10–1-11
Balancingparentheses/brackets, 1-15 lines, 7-15
Base lines in formats, 7-12
10 logarithms, 9-9 lines in listings, 5-8
array subscript, 3-10 Blanking zero values in PRINT, 7-10
e logarithms, 9-9
file position, 7-29 Block input from devices and files, 9-27
Block structures, 6-8
Base-16 integer constants, 3-7
Boolean
Base-2 integer constants, 3-7
operator definitions, 3-19
Base-8 integer constants, 3-7 operators, 3-19
Baud rate control, C-21 sets, 9-21 , 9-23
string operators, 4-10 , 4-13
BCD numbers and arithmetic, 3-2
Bracket matching, 1-15
BCD/IEEE floating point, 3-35
Bracket variable addressing, 9-38
Beginning value in variables, 3-8 Bracketed IF statements, 6-6
Bibliography of supplemental material, Branch trees, 6-8
1-5
Branching
Binary criteria, 6-10
conversion tables, D-9 out of CASE blocks, 6-9–6-10
data file access, 7-33 out of loops, 6-17
file access, 7-31 out of sequential execution, 6-1
floating point, 3-35 unconditionally, 6-2
format mode, 7-8
IMP, 4-11 Breaking out of loops, 6-17
integer constants, 3-7 Breaking program execution, 6-3 , 6-4
GFK-0256 Index-3
Index
Index-4 GFK-0256
Index
GFK-0256 Index-5
Index
Index-6 GFK-0256
Index
GFK-0256 Index-7
Index
Index-8 GFK-0256
Index
GFK-0256 Index-9
Index
Index-10 GFK-0256
Index
GFK-0256 Index-11
Index
Index-12 GFK-0256
Index
GFK-0256 Index-13
Index
Index-14 GFK-0256
Index
GFK-0256 Index-15
Index
Index-16 GFK-0256
Index
GFK-0256 Index-17
Index
Index-18 GFK-0256
Index
GFK-0256 Index-19
Index
Index-20 GFK-0256
Index
Modular Multi-level
arithmetic, 3-16 , 9-5 error control, 6-20
IF statements, 6-5 , 6-7
design, 8-1 , 8-21 , 10-1 , 10-15 , 10-24
program structures, 8-3 Multi-line, IF statements, 6-7
Module lookup order, 10-2 Multiple
file buffers, 7-26
Monitoring file positioning, 7-29
program execution–TRA CE command, formats, 7-11
2-34 line user-defined functions, 8-15
program variables, 2-31 number signs, 3-6
statement execution, 2-34 package development, 10-21
subroutine calls–CHECK command, package execution, 2-31
2-38 package programs, 2-3 , 10-1 , 10-3 , 10-4
Mouse support, C-13 , C-20
program access–SHOW command,
MOVE command, 2-25 2-41
Moving statements on a line, 1-10
data between variables, 5-9 workspaces, 1-8 , 10-24
data files, 7-25 Multiple MegaBasic environments–BA-
data from files, 7-29 SIC command, 2-45
data to files, 7-33 Multiplication, 3-16
data to memory, 7-44
Multiply string operator, 4-11
input characters, 1-15
memory contents, 7-44 , 9-37 Multiplying strings, 4-10 , 4-12
program lines, 2-24 , 2-25 Multi-user facilities, 6-24 , 6-26
the input cursor, 1-15
Multiuser file access, 7-38
MP/M-86 retriable errors, 6-23–6-24 Multiuser MegaBasic support, B-2 , B-5 ,
MS-DOS B-7
background processing, 7-54 Multi-way
environment strings, 9-38 decision branching, 6-8
multitasking, 7-54 GOTO statement, 6-3
subdirectories, 7-25
Multi-character input, 9-27
N
Multi-dimensional
numeric arrays, 3-10 Name
string arrays, 4-7 invocation, 8-9
listings, 2-23
Multi-level, string indexing, 4-21 of package containing error, 9-36
Multi-line re-assignment, 2-24
functions, 8-6 , 8-7 , 8-15 restrictions, 1-12 , D-6
print statements, 7-15 Name procedures, 8-12
procedures, 8-7
NAMES
Multi-package system example, 10-16 command, 1-12 , 2-23 , 2-24
GFK-0256 Index-21
Index
Index-22 GFK-0256
Index
GFK-0256 Index-23
Index
Index-24 GFK-0256
Index
GFK-0256 Index-25
Index
Index-26 GFK-0256
Index
GFK-0256 Index-27
Index
Index-28 GFK-0256
Index
GFK-0256 Index-29
Index
Index-30 GFK-0256
Index
GFK-0256 Index-31
Index
Index-32 GFK-0256
Index
GFK-0256 Index-33
Index
Index-34 GFK-0256
Index
GFK-0256 Index-35
Index
Index-36 GFK-0256