OpenVMS

OpenVMS runs on both DEC VAX and DEC Alpha computer systems. VAX systems typically use VAX-11C and Alpha systems use DEC-C. There are enough differences in the two compilers that separate DCL command files are supplied for each. The files COMPILE.COM and LINK.COM build Until using VAX-C. COMPILEA.COM and LINKA.COM build Until with DEC-C. Use the appropriate command files for your configuration.

COMPILE.COM compiles the modules in Until and a separate command file, LINK.COM, links the compiled object files. Type @COMPILE to compile the source. After the compile process finishes, type @LINK to run the linker and create the Until executable image. LINK.COM produces UNTIL_V.EXE and LINKA.COM creates UNTIL_A.EXE.


Configuring Until

Most of the configuration of the Until executable is done via symbols in COMPILER.H. The configuration symbols are:

Additionally, symbols that specify the compiler are set. #define the symbol to cause code to be included in Until or #undef the symbol to ignore it.

ANSI_CONSOLE
causes a small set of primitive functions that output ANSI escape sequences for cursor control. This is not as flexible as using termcap or terminfo. The words should be usable for a wide variety of systems, since many terminals are ANSI compatible, the PC with ANSI.SYS loaded, and Telnet generally supports ANSI escape sequences.

C_STRINGS
causes the C String library functions to be included in Until as primitives. See C Strings for the complete list of words. This module adds about 1300 bytes to the PC .EXE file.

CHECK_MALLOC
You have the option of selecting error testing of the return value from internal malloc() calls by defining CHECK_MALLOC

CIO_MODULE
includes the C File I/O library functions into Until as primitives. This module adds about 5500 bytes to the PC .EXE file.

DEBUG_PROG
controls whether the contents of DEBUG.C is compiled into Until. DEBUG.C contains simple C debugging functions that are generally not included in production versions.

FLOAT_POINT
The floating point package is compiled when FLOATING_POINT is #defined. The floating point implementation uses C double operations. This initial release contains only the basic math operations. It will be expanded in future releases.

IGNORE
Never define IGNORE. It is used as a convenient way for me to flag unused code in the source that I don't want to delete yet.

NEED_MEMMOVE
The function memmove() is not available in all C compilers; one gcc user on a SparcStation reported that memmove() was not included in link libraries and supplied a replacement. Define NEED_MEMMOVE if the memmove() is unresolved in the link step. The default is #undef.

READABLE
There are two approaches to coding many of the simple functions that use values from the parameter stack. One, the more readable has a local variable or two, calls popsp() to get the values from the parameter stack, performs the operation, and then puts the result back via a call to pushsp(). This is very readable and shows clearly what the intent of the function is. This is less than optimal because there are several function calls as well as creating a need for C to construct and remove a full stack frame.

A second approach is no local variables and either directly manipulate the pstack[] array, which eliminates both local variables and the calls to popsp() and pushsp(). This is more efficient, but much less readable.

A third approach in many cases is a nested pushsp() call with the entire function coded in a single line. This also uses no local variables.

The original approach to Until code was to make all of the source code as readable as possible, at the expense of efficiency. The time has now come to start optimizing Until. I am told that some C compilers do not create a stack frame if no local variables are used, which should result in even faster code. #define READABLE to compile the 'readable' version of the code. Otherwise, more efficient code will be substituted. There are only a few code optimizations in this version of Until. Expect more and more to be added with each release.

REPORT_STARTUP_ERRS
This symbol controls whether missing startup files, UNTIL.APP and APPLIC.APP, are reported in error messages. You have the option. The default is #undef.

SEAL_MODULE
Define this symbol to include the seal/unseal code. When SEAL_MODULE is defined, UNTIL.BIN is loaded at startup. When SEAL_MODULE is not defined, UNTIL.APP is loaded at startup. The default is #define SEAL_MODULE. This module adds about 2500 bytes to the PC .EXE file.

SEARCH_MODULE
Define SEARCH_MODULE to include the search and replace code. This code is loosely based on the concept of sub and gsub from AWK. This code is frequently used for file filters. The default is #define SEARCH_MODULE. This module adds approximately 2000 bytes to the PC .EXE file.

VECTORED_C_IO
Define this symbol causes all C-level console I/O functions to be vectored. The default action is identical to the non-vectored version, so unless you plan to actually revector the I/O, it is more efficient not to define VECTORED_C_IO. This code was contributed by Skip Carter.

VECTORED_IO
This symbol sets up vectored execution for a number of words. The default is #undef. See the discussion on for details of how Until implements vectored I/O execution.


7. PROGRAM DEVELOPMENT

This section gives some hints for program development with Until. As I use Until more and more, the more I believe that Until is a unique and very powerful approach to program development. There are several possible approaches for developing Until applications. These include:

Because of the completely different Until development options, System development must be approached from a slightly unconventional approach. (Does this surprise you?) The first decision is which approach to follow. Is application is going to be written directly in Until or to use Until as a macro language only. If you choose to write code in C, the question of coding style arises. You can make the C code look a lot like Forth. After all, dup(), over(), swap(), etc are all available as C functions. Or do you manipulate the data structures directly. Someone who knows Forth and not C will probably choose to use Until as merely another Forth compiler and the fluent C programmer will probably code large portions in C.


What You Need

The absolute minimum needed to run Until is until.exe. Running an application written in Until requires until.exe and an application file; applic.app by default. The application file can be specified on the command line or sealed into until.bin.

An application is generally kept in a .APP file during development then sealed into a .BIN file for distribution. The advantages of sealing an application include:

The process of sealing an application is relatively simple. First, run Until. At the initial program prompt, type:

  seal applic.app

applic.bin is created from applic.app.


Forth Programming

The bibliography identifies several good Forth resources, including books. Forth is a simple stack oriented, interactive programming language. It uses an RPN syntax ala HP calculators. General practice includes writing small words that do only one function, such as open a file or comparing a string. Test each word individually. In other words, write a few lines of code, then test it. That way, Forth words become building blocks that construct your application. This is an incredibly productive approach once you get past the learning curve.

Until is integrated with the OS from the Forth point of view. Program development can be done entirely within Until provided you are writing Forth code. Source can be edited, compiled, and tested without exiting back to the native OS as long as you don't crash the system. The following commands are available for development:

See the section on Operating System Words for more OS specific words.


Getting Along Without DOES>

Most Forth compilers includes a word, DOES> that is used to create new classes of words. For example:

  create array
       4 * allot
  does>
       4 * @ + ;

builds a new defining word, array, that defines an array of integers. An array is the defined as:

  10 array vector

40 bytes of memory would be allocated in the dictionary at compile time. At run time, the array entry number is converted to the address of the nth element and returned to the stack. Thus,

  2 vector

returns the address of the third element in the array. (The array is zero based in the example.)

The magic of Forth's create ... does> is the creation of new word types or classes. Words that use create ... does> compile a new word that has separate compile-time (from the create to the does>) and run-time (from the does> to the end of the definition).

In Until, the approach is writing a C function for the compile-time action and a separate function for run-time. The compile-time function, something like Compile_array() is bound to array as an immediate word by calling build_iprim() in USER.C. The run-time C function is bound to the Until word via build_prim(), also in USER.C.

Until's equivalent to DOES> is not quite as convenient traditional Forth's, but it gets the job done!


C Applications

Writing applications from the C prospective can lead to innovative applications. This approach allows those more comfortable with C than Forth to effectively utilize some of Forth's interactive nature. The bulk of your application code is coded as normal C functions using the proscribed interface to Until. (See Chapter 10 of [SMITH1] for more detailed discussions about adding new C primitives to Until.) pushsp() and popsp() are used to pass parameters from Forth to C functions.


Embedding Until

Until is designed from the ground up to be embedded in other application programs. A particularly good use is letting Until be the ``macro'' language for an application. If conditionals and looping are not required, much of the internal Until code can be eliminated. The user could build what are logically equivalent to display or execution list macros. These macros are really Forth words, but the RPN nature of Forth can easily be hidden from the user. One application I built had only about a dozen primitives available to the user, but the whole application was driven by user written macros. It was an elegant and simple solution. The final executable file was only 37k after removing unused internal Until code.

The mechanics of embedding Until in an application is covered in [SMITH2] and therefore only mentioned in passing here.

The process is almost as simple as it sounds. However, please keep the application code completely separate from Until code. Simply providing a couple of interface points is all that is really necessary. The next major release of Until will re-arrange the files slightly to make this a natural thing to do. The files LOCAL.H and LOCAL.C are the first step in this direction. There are several reasons for separating the code. These include:

When writing small applications it is easy to add a couple of variables to LOCAL.H and a function or two to LOCAL.C. I did this on the first large application that I built up on top of Until. I made a special effort to go back and clearly separate the Until code from the application code. That way, I could supply an Until object library instead of source code to the customer. Besides, it is just good programming practice.

The ease with which new primitives can be added to Until is and will probably remain one of the primary advantages that it has over any of its rivals; a new primitive can be added in just a couple of minutes excluding C compile time.


Embedding Details

#### UPDATE ####

This section contains the second cut at detailed instructions for embedding Until in another application. The problem is that with every release, things evolve. So, while the details published with the previous release may not be wrong, they are not 100% up to date at this point.

One big change since V2.2 is the addition of a hook at the end of the Outer Interpreter to call a user supplied function. This provides a convenient place to perform initialization after an ABORT.

Note that each call to outer() does a warm start, which clears the parameter stack. Until's main() is set up this way. Refer to it as an example.


Development Style

Application development style is mentioned in the main description for this chapter. This section attempts to address style is some detail. Until is unique and takes some additional thought to take fullest advantage of what we have here for developing applications.

The picture in my mind is going to be hard to describe. When writing a Forth program, the programmer sits at the computer with the vendor supplied Forth manual and maybe a copy of Starting Forth for reference when coding.

A C programmer also has reference material close at hand too, probably a copy of The C Programming Language and the function library manual when coding. When coding in either language deciding on program partitioning is a matter of figuring out what module a word or function logically belongs in.

Developing an Until application that takes fullest advantage of the marriage between Forth and C has an additional level of development decisions. Should this operation be a Forth word or a C function? How are the C and Forth pieces going to interface? What primitives should the user be allowed to see? Conscious effort is required for language choice on a function by function basis, instead of once for the whole application. I believe this is a very powerful combination with a great deal of potential, but it is an art for the time being.

Part of the ``art'' is deciding what Forth words the user can access to build application macros. Some applications require a relatively complete language but, many can get by with only a handful. One system I built around Until contained only eight to ten words. The system was customizable, quite powerful, and simple from the user prospective.

Since Until based applications are really C programs with user macros (at least that is what the customer thinks...), calling the user level word set a ``language'' has always had bad connotations in my mind. First, I don't want the customer asking me what the macro language is. Secondly, calling it an application language will probably confuse more users than help describe what it is.

While rereading Thinking Forth [BRODIE1], I ran across the term ``lexicon''. Brodie uses lexicon to describe the application specific set of words the user has access to. In my mind, it describes exactly the situation of Until being embedded in an application. The user has a lexicon of application specific commands to construct macros with. Learning a few simple commands is perceived as much easier than learning a new language by users.

The point of this whole rambling dissertation is that designing the user lexicon is an important part of building an application around Until. In turn, I believe this is where much of the potential power of Until as an application development tool comes from.


Table of Contents
Next Section