The GNU C4x Tools User's Guide

13 April 1999

Michael Hayes


What are the GNU C4x-Tools?

The GNU C4x-Tools are a suite of programs for compiling, assembling, linking, running, and debugging programs for the TMS320C3x and TMS320C4x Digital Signal Processors (DSP)s.

How to compile and run a simple program

Here's the ubiquitous hello world program written in C:

#include <stdio.h>

int main(int argc, char **argv)
{
    printf("Hello world!\n");
    return 0;
}

and here is a simple Makefile to build this program for a C30 target system:

hello.out: hello.c
	c4x-gcc -m30 -msmall -g -Wall -O2 -o $ $^

clean:
	-rm -f *.o *.out *.s *.i *~

where

c4x-gcc
is the name of the GCC driver program for the C4x target.
-m30
says to generate code for the C30 CPU.
-msmall
says to generate code for the small memory model.
-g
says to save symbols in the output file for debugging.
-Wall
says to warn about all the common problems.
-O2
says to generate code using optimisation level 2.

Saving the example program as a file called `hello.c' and then running `make' will generate an executable file `hello.out' that can be loaded and run on a C30 target system.

Running this executable file is more difficult. Somehow it needs to be loaded into the memory of the target system and have control passed to it. With many target systems the program will require to be programmed into EPROM, however for the moment, let's consider a target system with a host interface that allows the program to be downloaded to the target system for the host computer (this may be via a serial interface, shared memory, an ethernet connection, etc.). The C4x-Tools program to download and run a program is `c4x-run', and this can be invoked as:

$ c4x-run hello.out

Note that in many cases this program will not print anything since most embedded systems do not have an output screen on which the message can be displayed. However, some embedded operating systems will provide a means to output the message to a host system (see ???).

How to compile a program with GCC

Invoking the compiler

To compile a program for the C30 or C40 it is necessary to invoke GCC as a cross-compiler (since the generated code will not run on the system that runs the compiler). This can be achieved by invoking the driver `gcc' with the -b c4x option:

$ gcc -bc4x hello.c

However, it is sometimes necessary to specify the version number -V version if the native GCC compiler and the C4x cross compiler are different versions of `gcc'. An alternative approach is to invoke the c4x cross compiler specifically, for example:

$ c4x-gcc hello.c

Note that `/usr/bin/gcc' is a generic driver which uses the `-b TARGET' switch (and `-V VERSION' switch) to look up the actual compiler for the desired target (and version), `/usr/lib/gcc-lib/TARGET/VERSION/cc1'. Thus many different versions of GCC can coexist for different targets. The default options are supplied from a specs file, `/usr/lib/gcc-lib/TARGET/VERSION/specs'. Give the -v switch to GCC to see which specs file it uses (and what other files it uses). If your prefix used for compilation is not `/usr', the above paths change accordingly.

C4x compiler options

These `-m' options are defined for the C4x:

-m30 -mc30
Generate code for the TMS320C3x family.
-m40 -mc40
Generate code for the TMS320C4x family. This is the default.
-mbig-memory
-mbig
-msmall-memory
-msmall
Generates code for the big or small memory model. The small memory model assumed that all data fits into one 64K word page. At run-time the data page (DP) register must be set to point to the 64K page containing the .bss and .data program sections. The big memory model is the default and requires reloading of the DP register for every direct memory access.
-mbk
-mno-bk
Allow (disallow) allocation of general integer operands into the block count register BK.
-mdb
-mno-db
Enable (disable) generation of code using decrement and branch, DBcond(D), instructions. This is enabled by default for the C4x. To be on the safe side, this is disabled for the C3x, since the maximum iteration count on the C3x is 2^23 + 1 (but who iterates loops more than 2^23 times on the C3x?). Note that GCC will try to reverse a loop so that it can utilise the decrement and branch instruction, but will give up if there is more than one memory reference in the loop. Thus a loop where the loop counter is decremented can generate slightly more efficient code, in cases where the RPTB instruction cannot be utilised.
-mdp-isr-reload
-mparanoid
Force the DP register to be saved on entry to an interrupt service routine (ISR), reloaded to point to the data section, and restored on exit from the ISR. This should not be required unless someone has violated the small memory model by modifying the DP register, say within an object library.
-mmpyi
-mno-mpyi
For the C3x use the 24-bit MPYI instruction for integer multiplies instead of a library call to guarantee 32-bit results. Note that if one of the operands is a constant, then the multiplication will be performed using shifts and adds. If the -mmpyi option is not specified for the C3x, then squaring operations are performed inline instead of a library call.
-mfast-fix
-mno-fast-fix
The C3x/C4x FIX instruction to convert a floating point value to an integer value chooses the nearest integer less than or equal to the floating point value rather than to the nearest integer. Thus if the floating point number is negative, the result will be incorrectly truncated an additional code is necessary to detect and correct this case. This option can be used to disable generation of the additional code required to correct the result.
-mrptb
-mno-rptb
Enable (disable) generation of repeat block sequences using the RPTB instruction for zero overhead looping. The RPTB construct is only used for innermost loops that do not call functions or jump across the loop boundaries. There is no advantage having nested RPTB loops due to the overhead required to save and restore the RC, RS, and RE registers. This is enabled by default with -O2.
-mrpts=count
-mno-rpts
Enable (disable) the use of the single instruction repeat instruction RPTS. If a repeat block contains a single instruction, and the loop count can be guaranteed to be less than the value count, GCC will emit a RPTS instruction instead of a RPTB. If no value is specified, then a RPTS will be emitted even if the loop count cannot be determined at compile time. Note that the repeated instruction following RPTS does not have to be reloaded from memory each iteration, thus freeing up the CPU buses for oeprands. However, since interrupts are blocked by this instruction, it is disabled by default.
-mloop-unsigned
-mno-loop-unsigned
The maximum iteration count when using RPTS and RPTB (and DB on the C40) is 2^31 + 1 since these instructions test if the iteration count is negative to terminate the loop. If the iteration count is unsigned there is a possibility than the 2^31 + 1 maximum iteration count may be exceeded. This switch allows an unsigned iteration count.
-mti
Try to emit an assembler syntax that the TI assembler (asm30) is happy with. This also enforces compatibility with the API employed by the TI C3x C compiler. For example, long doubles are passed as structures rather than in floating point registers.
-mregparm
-mmemparm
Generate code that uses registers (stack) for passing arguments to functions. By default, arguments are passed in registers where possible rather than by pushing arguments on to the stack.
-mparallel-insns
-mno-parallel-insns
Enable the optimization pass which generates parallel instructions. This is enabled by default with -O2.
-mparallel-mpy
-mno-parallel-mpy
Allow the generation of MPY||ADD and MPY||SUB parallel instructions, provided -mparallel-insns is also specified. These instructions have tight register constraints which can pessimize the code generation of large functions.
-maliases
-mno-aliases
This switch informs GCC that there are potential memory aliases when combining parallel instructions. This option is set by default. Use -mno-aliases if you are sure that memory references are not aliased so that GCC can generate parallel instructions with a memory store.

Predefined macros

_C3x, _TMS320C3x
Expands to 1 if target is a C3x, undefined otherwise.
_C30, _TMS320C30
Expands to 1 if target is a C30, undefined otherwise.
_C31, _TMS320C31
Expands to 1 if target is a C31, undefined otherwise.
_C32, _TMS320C32
Expands to 1 if target is a C32, undefined otherwise.
_C4x, _TMS320C4x
Expands to 1 if target is a C4x, undefined otherwise.
_C40, _TMS320C40
Expands to 1 if target is a C40, undefined otherwise.
_C44, _TMS320C44
Expands to 1 if target is a C44, undefined otherwise.
_REGPARM
Expands to 1 if the register argument model is in use, undefined otherwise.
_BIGMODEL
Expands to 1 if the big memory model is in use, undefined otherwise.

The Compilation Process

The program `gcc' (or `c4x-gcc') is just a driver program that invokes the C preprocessor `cpp', the C compiler `c4x-cc1', the assembler `c4x-as', and the linker `c4x-ld' in turn. The behaviour of the `gcc' driver is controlled by a `specs' file which can be determined by running `gcc' with a -v option, for example:

$ c4x-gcc -v hello.c

Reading specs from /usr/lib/gcc-lib/c4x/egcs-2.93.15/specs
gcc version egcs-2.93.15 19990404 (gcc2 ss-980929 experimental)

Note that there is a different `specs' file for every version of each cross compiler installed on the system. You can ignore the contents of these files since you should never need to modify them.

The -v (verbose) option to `gcc' can also be used to see what actions the `gcc' driver performs and where it looks for files.

$ c4x-gcc -v -m30 -msmall -O2 hello.c -o hello.out

Reading specs from /usr/lib/gcc-lib/c4x/egcs-2.93.15/specs
gcc version egcs-2.93.15 19990404 (gcc2 ss-980929 experimental)
 /usr/lib/gcc-lib/c4x/egcs-2.93.15/cpp -lang-c -v -undef -D__GNUC__=2 \
 -D__GNUC_MINOR__=93 -D__OPTIMIZE__ -D_TMS320C3x -D_C3x -D_TMS320C30 \
 -D_C30 -D_REGPARM -U_BIGMODEL hello.c /tmp/ccAFdaaa.i
GNU CPP version egcs-2.93.15 19990404 (gcc2 ss-980929 experimental) \
 (TMS320C[34]x, TI syntax)
#include "..." search starts here:
#include <...> search starts here:
 /usr/lib/gcc-lib/c4x/egcs-2.93.15/include
 /usr/lib/gcc-lib/c4x/egcs-2.93.15/../../../../c4x/include
End of search list.
The following default directories have been omitted from the search path:
 /usr/lib/gcc-lib/c4x/egcs-2.93.15/../../../../include/g++-2
 /usr/lib/gcc-lib/c4x/egcs-2.93.15/../../../../c4x/sys-include
End of omitted list.
 /usr/lib/gcc-lib/c4x/egcs-2.93.15/cc1 /tmp/ccAFdaaa.i -quiet -dumpbase \
  hello.c -m30 -msmall -O2 -version -o /tmp/cc0ahaaa.s
GNU C version egcs-2.93.15 19990404 (gcc2 ss-980929 experimental) (c4x) \
  compiled by GNU C version 2.7.2.3.
 /usr/c4x/bin/as -m30 -r -s -o /tmp/ccqGkaaa.o /tmp/cc0ahaaa.s
 /usr/lib/gcc-lib/c4x/egcs-2.93.15/collect2 --architecture c3x -o hello.out \
  /usr/c4x/lib/crt0_3sr.o -L/usr/lib/gcc-lib/c4x/egcs-2.93.15/c3x/small \
  -L/usr/lib/gcc-lib/c4x/egcs-2.93.15 -L/usr/c4x/lib /tmp/ccqGkaaa.o -lgcc -lc -lgcc

Looking at this output, it can be seen that `gcc' runs the C preprocessor `cpp' on the source file `hello.c' (specifying a number of macro definitions indicating which CPU model to generate code for, etc.). Note that `cpp' prints out the directories where it searches for header files to include.

The resultant temporary file output by `cpp' is then passed to the C cross compiler `cc1' which converts the preprocessed source code into assembler code that is stored in another temporary file that gets passed to the assembler `as'. This then generates a temporary object file that is linked with the system libraries by `ld'. Note that usually `ld' is a link to a program `collect2' which handles grouping of constructor functions for C++.

One aspect that confuses newcomers to these tools is that there are often two copies of each of the tools, eg, `/usr/bin/c4x-as' and `/usr/c4x/bin/as'. The former is usually invoked from a shell command while the latter is usually invoked from the `gcc' driver.

Supported languages

EGCS supports the C, C++, Objective C, Chill, Java, and Fortran 77 (g77) programming languages. However, Fortran currently won't work as a cross-compiler, Java is awaiting run-time libraries, and I haven't tried Objective C and Chill (an embedded systems language) with the C4x back-end. Front-end patches are also available for Pascal (gpc) and Ada95 (gnat) although I haven't tried these with the C4x back-end either (I've heard of an existence of a Modula-2 front end and several other front ends for more esoteric languages).

Code Generation

The code generated by GCC can be examined by using the compiler option -S. This will stop the `gcc' driver from assembling and linking the generated code. Alternatively, the option --save-temps can be used to prevent GCC from deleting all its temporary intermediate files.

Low overhead looping instructions

Wherever possible, GCC tries to emit the low overhead looping instructions RPTB/RPTS and DB to speed up the execution of loops.

Parallel instructions

The generation of parallel instructions is often foiled if GCC detects the presence of a possible memory alias. Whereas Fortran assumes that a programmer will not call a function with aliased arrays, C does not make this assumption and has to assume the worst (the restrict qualifier in the forthcoming new C standard will help here).

If you know that your functions are never called with aliased pointers, you can use the compiler option -fargument-noalias. Most of the time this shouldn't cause any trouble. However, problems can occur with routines such as when the pointers a and b point to the same array, as with the following example:

void foo(double *a, double *b, double c, int size)
{
    int i;

    a[0] = b[0];
    for (i = 1; i < size; i++)
        a[i] = a[i - 1] + b[i] * c;
}

When compiled with gcc -bc4x -O2 -S, `gcc' generates:

        addi3      -2,r1,rc
        rptb       L7-1
L5:
        mpyf3      f2,*ar0++,f0
        addf3      f0,*-ar2(1),f0
        stf        f0,*ar2++
L7:

while gcc -bc4x -O2 -fargument-noalias -S generates:

        addi3      -3,r2,rc
        mpyf3      f1,*ar0++,f0
        cmpi3      0,rc
        blt        L7
        rptb       L7-1
L5:
        mpyf3      f1,*ar0++,f0
||      addf3      f0,*-ar2(1),f3
        stf        f3,*ar2++
L7:
        addf3      f0,*-ar2(1),f3
        stf        f3,*ar2++

Note that if ar0 and ar2 are pointing to the same array at the start of this code snippet, then the MPY||ADD instruction will load the old value at ar0 but not the new value that is to stored at the end of the loop. Similar problems can occur with the following program if called as

fcopy1(foo + 1, foo, 128)

where

float fcopy1(float *a, float *b, int size)
{
    int i;
    
    for (i = 0; i < size; i++)
        a[i] = b[i];
    return 1;
}

How to use the assembler

The GNU assembler for the c4x is `c4x-as' and this can be invoked to assemble assembler code into an object file.

How to link object files into an executable

The linking of object files into an executable is performed by the program `c4x-ld', although this is usually invoked through the GCC driver `gcc'.

Linker files

To be able to create an executable, `c4x-ld' requires a number of files:

`libgcc.a'
This is a library used by GCC for functions that are not worthwhile to code inline, such as integer and floating point divide operations.
`crt0_xxx.o'
This contains the main entry point _start and needs to transfer control to the operating system (if present) or to call the bootstrap routine which sets up the target system and calls the function main.
`libc.a'
This is the C run-time library. It is not always required.
`linker script'
This is used by the linker to determine where to place the program sections in memory. This can be specified by the -T option. The default script resides in `/usr/c4x/lib/ldscripts'.

Optionally other libraries may be required such as the math library `libm.a' for operations such as sqrt and pow.

Linker script

The default linker script is `/usr/c4x/lib/ldscripts/c4xcoff.x'. It defines where all the program sections are to reside in memory. This linker script includes one non-standard program section .comms which is a piece of shared memory that my operating system using to communicate to the host PC. The other program sections are:

.bss
This section is where uninitialised global variables are stored (bss stands for block started by symbol--an old IBM assembler directive). Note that these variables may not be zeroed if your run-time system does not explicitly do this.
.data
This section is used for the storage of initialised global variables.
.const
This section is used for constant data such as strings and immediate constants that are too large to be stored within an opcode.
.cinit
This section is not used by GCC. The TI compiler uses this to store initialisation data for executables that are to reside in ROM.
.text
This section stores all the executable machine opcodes.
.init
Not used by GCC for the C4x.
.fini
Not used by GCC for the C4x.
.ctors
This sections stores lists of C++ constructors.
.dtors
This sections stores lists of C++ destructors.
.stack
This section reserves space for a system stack.
.sysmem
This section reserves space for a system heap (used by malloc).

The default linker script assumes that there is sufficient contiguous memory starting from address 0 to accomodate the desired program. Note that the .bss and .const (and .data if it is used) sections must be contiguous for the small memory model and their combined size must fit within a 64k page.

OUTPUT_FORMAT("coff-c4x")
 SEARCH_DIR(/usr/c4x/lib);
ENTRY(_start)
 __SYSMEM_SIZE = DEFINED(__SYSMEM_SIZE) ? __SYSMEM_SIZE : 0x4000;
 __STACK_SIZE  = DEFINED(__STACK_SIZE)  ? __STACK_SIZE  : 0x1000;
SECTIONS
{
  .comms  64 : {
    *(.comms)
  } 
  .bss  SIZEOF(.comms) + ADDR(.comms) : {
     .bss  =  .;        
    *(.bss)
    *(COMMON)
      end  =  .;
      _end  =  end;
  }
  .data  SIZEOF(.bss) + ADDR(.bss) :
  {                                 
      .data  = .;
    *(.data)
      edata  =  .;
  }
  .const  SIZEOF(.data) + ADDR(.data) :
  {                                         
    *(.const)
  }
  .cinit  SIZEOF(.const) + ADDR(.const) :
  {                                         
      cinit = .;
    *(.cinit)
  }
  .text  SIZEOF(.cinit) + ADDR(.cinit) : {
      .text =  .;
     *(.init)
    *(.text)
     ___CTOR_LIST__ = .;
     LONG(___CTOR_END__ - ___CTOR_LIST__ - 2)
     *(.ctors)
     LONG(0);
     ___CTOR_END__  = .;
     ___DTOR_LIST__ = .;
     LONG(___DTOR_END__ - ___DTOR_LIST__ - 2)
     *(.dtors)
     LONG(0)
     ___DTOR_END__  = .;
     *(.fini)
      etext =  .;
      _etext =  etext;
  }
  .stack  SIZEOF(.text) + ADDR(.text) :
  {                                         
    *(.stack)
     .  =  . + __STACK_SIZE;                
  }
  .sysmem  SIZEOF(.stack) + ADDR(.stack) :
  {                                         
    *(.sysmem)
  }
  .heap  SIZEOF(.sysmem) + ADDR(.sysmem) :
  {                                         
     . += __SYSMEM_SIZE - SIZEOF(.sysmem);
  }
  .stab  0 (NOLOAD) : 
  {
    [ .stab ]
  }
  .stabstr  0 (NOLOAD) :
  {
    [ .stabstr ]
  }
/* The TI tools sets cinit to -1 if the ram model is used.  */
      cinit = SIZEOF(.cinit) ? cinit : -1;
}

The following symbols are defined by this script (using the same definitions as the TI tools):

.bss Begin of .bss section.
end End of .bss section.
.data Begin of .data section.
edata End of .data section.
.text Begin of .text section.
etext End of .text section.
.cinit Begin of .cinit section.

Object file utilities

The contents of object files can be dumped in a variety of formats using `c4x-objdump'. For example, the option -d can be used to dump the disassembled contents of the object file.

$ c4x-objdump -d hello.out

hello.out:     file format coff-c4x

Disassembly of section .text:
0000919d <_start> br 000091de <_c_int00>
0000919e <_main> call 000091d8 <___main>
0000919f <_main+1> addi 1,sp
000091a0 <_main+2> ldiu sp,ar0
000091a1 <_main+3> addi 1,ar0
000091a2 <_main+4> ldiu @000085ea <edata+7>,rs
000091a3 <_main+5> sti rs,*-ar0(1)
000091a4 <_main+6> call 0000b520 <_printf>
000091a5 <_main+7> ldiu 0,r0
000091a6 <_main+8> addi -1,sp
000091a7 <_main+9> retsu
000091a8 <___do_global_dtors> push ar4
000091a9 <___do_global_dtors+1> ldiu @000085eb <.const>,ar1
000091aa <___do_global_dtors+2> ldiu *ar1,ar0
000091ab <___do_global_dtors+3> ldi *ar0,r0
000091ac <___do_global_dtors+4> beqd 000091bb <.ef>
000091ad <___do_global_dtors+5> ldiu ar1,ar4
000091ae <___do_global_dtors+6> nop r0

For further information about `c4x-objdump', see the generic `objdump' info pages.

The symbol table can be stripped from an object file using `c4x-strip'. This can make the executable file size much smaller and thus save disk space. However, this won't reduce the memory requirements of the program and will make the program difficult to debug.

The size of the main program sections can be displayed with `c4x-size', for example:

$ c4x-size hello.out
text        data        bss        dec        hex        filename
10788          3485           54208          68481          10b81          hello.out

Symbols within object files can be examined with `c4x-nm', for example:

$ c4x-nm hello.o
00000000 b .bss
00000000 ? .const
00000000 d .data
00000000 t .text
         U ___main
00000000 T _main
         U _printf
00000000 d data_sec
00000000 t gcc2_compiled.

Library file utilities

Library files can be created with the archiver `c4x-ar'.

For further information about `c4x-ar', see the generic `ar' info pages.

An index can be added to the library archive using `c4x-ranlib'.

How to debug a program with GDB

$ c4x-gdb hello.out 
(gdb) target c4x

The C library

Currently there are no freely available C and math libraries for the C30 or C40 DSPs. Texas Instruments sells these libraries with their compiler (they are not particularly well written but work tolerably well) and this is what I currently use. However, I cannot redistribute it. The alternatives are targetting the GNU C library (glibc) or newlib (from Cygnus Solutions) to the C30/C40. I would recommend the latter since it has been used for a number of embedded processors and is a much more simple library than glibc.

If you use the Texas Instruments C library (`rts30.lib' or `rts40.lib'), you will have to assemble a small file `crt0.s'...

Downloading executable files to the target system

This is a target system dependent operation. There is a generic loader program `c4x-load' that can be used to download an executable file and this is configurable to support different target systems. An example of its use is:

$ c4x-load hello.out

Another generic program `c4x-reset' can be use to reset the target system.

Configuring and Installing C4x-Tools

Building

The installation of egcs can be achieved along the following lines:

  1. Acquire and unpack latest egcs snapshot:
    $ tar xvfz egcs-ss-xxxxx.tgz
    
  2. Patch latest egcs snapshot with latest c4x patches.
  3. Create a build directory:
    $ mkdir egcs-obj
    $ cd egcs-obj
    
  4. Configure egcs:
    $ ../egcs/configure --target=c4x --prefix=/usr
    
    (Note that if you want GCC installed in `/usr/local/lib/lib-gcc', then use --prefix=/usr/local which is the default.)
  5. Build egcs:
    $ make LANGUAGES="c c++"
    
    (Note that just typing make will fail when trying to build the libraries `libchill,' `libf2c', and `libobjc' for the languages Chill, Fortran, and Objective C respectively. It will also fail when checking for the executable suffix unless you have a C run-time start up file `crt0_4br.o' somewhere accessible.)
  6. Install egcs:
    $ make install LANGUAGES="c c++"
    

Testing

The compiler can be tested using the `dejagnu' testing harness. This can be installed as follows:

  1. Acquire and unpack latest dejagnu snapshot (ftp://egcs.cygnus.com/pub/egcs/infrastructure)
  2. Create symbolic link (or copy `dejagnu' subdirectory) into toplevel `egcs' directory. tree.
    $ ln -s dejagnu-xxx/dejagnu egcs/dejagnu
    
  3. If TCL or expect is not installed on your system, create links for the `tcl' and `expect' subdirectories.
    $ ln -s dejagnu-xxx/tcl egcs/tcl
    $ ln -s dejagnu-xxx/expect egcs/expect
    
  4. Reconfigure and rebuild EGCS. Note that since `dejagnu' uses TCL, no compilation is required.
  5. Try make check to see if dejagnu runs. It will bleat that the target board is not defined but it should run the gcc c-torture compilation tests using unix.exp.
    $ make check
    
  6. If you want to test your target system then you will need to create a `site.exp' file and point the DEJAGNU environment variable to it.
    $ export DEJAGNU=~/site.exp
    
    where an example `site.def' file is:
    case "$target_triplet" in {
        { "c4x-*" } {
    #        set target_list { c4x-sim30 c4x-dspt30 }
             set target_list { c4x-sim30 }
        }
    }
    

Files

EGCS files

`c4x-gcc C compiler'
`c4x-c++ C++ compiler'

Binutils files

`c4x-ar Archiver'
`c4x-as Assembler'
`c4x-ld Linker'
`c4x-nm List symbols in object file'
`c4x-objcopy Object file conversion utility'
`c4x-objdump Object file dump utility'
`c4x-ranlib Add index to archive'
`c4x-size Dump size of program sections'
`c4x-strip Strip symbols form object file'

GDB files

`c4x-gdb'

Utils files

`c4x-dbg Debug server'
`c4x-load Loader'
`c4x-reset Reset target CPU'
`c4x-run Load and run a program'

Index

a

b

c

d

e

f

g

h

i

l

m

o

p

r

t

u


This document was generated on 15 April 1999 using the texi2html translator version 1.51a.