Start-up Code

In previous versions of TADS, when the program started running, TADS called an initialization function (called init) to carry out any special start-up processing that the program wanted to perform.  This function performed any needed initialization, then returned.  The system then entered a command loop, in which the system prompted the user, read a command line, parsed the command, and called program functions and methods to execute the operations of the command.

 

TADS 3 does not have a built-in parser, and it doesn't even have a built-in command loop.  Instead, the program is responsible for all of this.  At start-up, the VM calls an "entrypoint" function (called _main).  This function is called the entrypoint because it's the point at which control enters the program.  The _main function does not return until the program has finished executing.  Once _main returns, TADS 3 terminates the program.

 

In this respect, TADS 3 is more like a traditional programming language (such as Java or C++) than it is like previous versions of TADS.  This approach requires some extra coding in the game program, but it provides much greater flexibility, since the program is no longer forced to work with a pre-defined command loop.  Besides, any extra code needed for start-up will eventually be available from libraries, so most game authors won't have to code this part of the program anyway.

 

The _main function receives one argument, which is a list giving the strings making up the command line.  The first entry in this list is the name of the program image file; the subsequent entries in the list are the strings that follow the image file name on the command line that started the program.  The rules for parsing command lines vary from system to system, but on most systems, arguments are delimited by spaces unless the arguments are enclosed in quotation marks.  For example, this command line:

 

   t3run mygame "first argument" second third

 

Would yield a list with four elements on most systems:

 

   'mygame.t3'
   'first argument'
   'second'
   'third'

 

The _main routine normally also sets up the default display function, so that double-quoted strings and embedded text can be displayed.  See Default Display Function for details.

 

Note that most libraries will probably include their own VM-level (_main) entrypoint implementation.  This is the primary reason for the odd name: most libraries will probably provide standard implementations of _main(), which will set up and then call another function, to be provided by the user code.  Library-provided _main() functions will probably want to set up a top-level error handler (via the try statement), check the pre-initialization mode, possibly process the command-line arguments or other environmental data, and call a secondary entrypoint function provided by user code.  This is akin to the way most C compilers work: the compiler's standard run-time library provides a low-level start-up routine, which serves as the entrypoint from the operating system; this function normally does some initial processing, then calls the main() function defined by the program.

Default Start-Up Code

TADS comes with source code for a default start-up module.  This code is in a file called _main.t, in the same directory as the compiler.  By default, t3make will automatically include this module in your program, which makes it unnecessary for you to add the module to your build explicitly.

 

The code in _main.t implements several functions and objects that most TADS 3 programs will need.  Most programs will have no reason to modify the default versions provided in _main.t, which is why t3make includes the file in all builds by default; however, if you do need to replace this module, you can use the –nodef compiler option to tell the compiler not to include this default module.

 

The functions and objects implemented in _main.t are:

 

All of this is done within a try/catch block; if any exceptions are caught at this level, the catch block displays the exception's error message and terminates the program.

 

When you use the default start-up code, you must define the main(args) function.  This function is your program's main entrypoint; the default start-up code calls main(args) after performing its initialization steps, as described above.  "args" is a list of strings giving the arguments to the program.

Pre-initialization

With TADS 2, if your game program defined a function called preinit(), the compiler called this function after completing compilation and before writing the binary .GAM file. TADS 3 has a similar mechanism but implements it differently.

 

Note that, if you're using the default start-up code, you should perform pre-initialization by defining one or more PreinitObject instances.  Refer to library pre-initialization for details.

 

In TADS 3, after linking is complete, when not compiling for debugging, t3make calls the main entrypoint function (_main), just as it would for normal execution.  However, the system sets a special internal flag that indicates that it's performing pre-initialization rather than a normal run of the program.  You can access this flag via the t3GetVMPreinitMode() function in the "t3vm" function set; this function returns true if pre-initialization is taking place, nil during normal execution.

 

When you compile for debugging (with the "-d" flag to t3make, for example), the compiler does not perform pre-initialization.  Instead, this step is deferred until you run the program with the interpreter.  This difference is important because it allows you to step through the pre-initialization code with the debugger when you compile for debugging; if pre-initialization were to run as part of compilation for a debug build, you wouldn't have a chance to trace through that code.

 

Note that the difference between the debug and non-debug build modes does not mean that you have to write your code differently for the two modes.  Pre-initialization runs in both types of builds, and runs in the same sequence in both types of builds; as far as your program is concerned, there's no difference at all.  The only difference is where the pre-initialization step is carried out.  Here's a summary of the sequence of events in the two cases:

 

Normal (non-debug) builds:

·           Compiler:
o          Compiles program
o          Runs pre-initialization
·           Interpreter:
o          Executes the main program
 
Debug builds:

o          Compiles program

o          Runs pre-initialization

o          Executes the main program

 

In both cases, the sequence of events that your program sees is the same: compile, pre-initialize, run.