Reference

mknbi-mgl(8)

MaKe NetBoot Image for MGL program

NAME

mknbi-mgl - MaKe NetBoot Image for MGL program

SYNOPSIS

mknbi-mgl [OPTIONS] [<source file> [output file>]]

DESCRIPTION

mknbi-mgl converts a program, written in the Pascal-like Menu Generation Language (or MGL for short), into a boot image file which is directly loadable by diskless systems using a Boot-Rom.

OPTIONS

-0, --opt86

Make an output file suitable for a 8086 processor.

-1, --opt186

Make an output file suitable for a 80186 or higher processor.

-3, --opt386

Make an output file suitable for a 80386 or higher processor.

-b, --batch-sys SYS

Lets mknbi-mgl operate in batch mode. It will look up the given system name in a database file and generate a netbootable image according to the parameters given in the database entry. If this option is not given, mknbi-mgl will operate according to the other command line options. If giving any other options in addition to -b or --batch-sys, the parameters found in the database will override those processed from the command line.

-c, --noheader

Do not write a boot image header into the output file. When using this option, it's possible to run the output file as a .COM program under DOS.

-C, --config-file FILE

Specifies a configuration file.

-d, --debug

Include debugging information into the output file. This will increase the size of the output file, but will give the source file line number when a runtime error occurs. There is no significant decrease in execution speed.

-h, --help

Print short usage information.

-i, --infile FILE

Path name of input file. If not given, standard input is used.

-o, --outfile FILE

Pathname of output file, which is lateron loaded by the Boot-Rom via tftp.

-v, --version

Print version information.

-x, --verbose

Verbose output to let you know what mknbi-mgl is doing. Each -x increases the verbosity level.

MGL SYNTAX

The Menu Generation Language MGL is very similar to the Pascal programming language. However, it does not support the full Pascal command set, while it adds some commands which allow an easier design of menus.

Comments

Since the standard Pascal comment character is already used in a different context with MGL, comments can be delimited with (* and *). In addition everything following a hash character # is treated as a comment until the end of the line. This can be used in conjunction with the C preprocessor.

Reserved words

The following words are reserved and can not be used as identifiers: array, begin, break, const, default, do, else, end, function, if, item, of, procedure, record, repeat, restart, return, screen, select, then, type, until, var, while, with. Unlike Pascal MGL is case sensitive.

General layout of a program

A MGL program consists of declaration blocks like Pascal. Those declaration blocks are constant declarations, type declarations, variable declarations, function declarations and procedure declarations. In addition to Pascal, MGL also knows about screen declarations. A screen is basically the same as a procedure without any arguments. A program has to have at least one screen declaration at the end, which gets run when the program starts. All declaration blocks can be mixed and don't have to follow in any specific order with the exception of the start screen which has to always come last. Each block has to be terminated by a semicolon, again with the exception of the last screen, which has a dot for termination. Each declaration block is introduced by a keyword:

const

Define symbols with constant values. Each such definition has to look like:

<constname> = <value>;

The value can be any scalar or string expression, but has to be constant. Variable names in constant declarations are not allowed. For string constants the string value has to be delimited by double quotes, which is unlike Pascal. You can use a backslash to define special characters within the string:

\\r

Carriage return

\\t

Tab

\\n

New line

\\b

Back space

\\f

Form feed

\\<octal number>

Character represented by the octal number

Character constants are specified using single quotes, while it is possible to use the same backslash notation as with strings. However, the length of a character constant cannot exceed one character.

Boolean constants are represented by the values true and false. These are predefined constant values, where the ordinal number of false is 0, and the ordinal number of true is 1. Note that unlike Pascal, MGL is case sensitive, and these boolean constant values have to be given in all lower case.

type

Define a new type identifier. Assigning a type to an identifier looks like:

<typename> = <type>;

See below for a further discussion about types.

var

Define a new variable. The syntax is:

<varname> : <type>;

This will generate a new variable with the name <varname> of type <type>.

function

A function is defined the same way as in Pascal. Following the keyword function the function's name has to be given, followed by the arguments in round brackets, a colon and the return type of the function:

function <funcname> ( <arguments> ) : <type>;

The arguments are specified like variable definitions, seperated by semicolons. By default, all arguments to a function are passed by value, including non-scalar arguments like strings. If the var keyword is given in front of an argument specification, that particular argument is passed by reference instead. Since passing an argument by value requires copying it onto the stack (which is space and time consuming with strings), there is an additional keyword: const. If it is given instead of the var keyword in front of an argument specification, this argument is passed to the function by value, but cannot be altered by the function. This is an extension to the Pascal standard. Unlike Pascal, MGL does not support functions or procedures as arguments.

Within a function, the name of the function serves as a variable for the return value. Therefore, assigning a value to <funcname> specifies the return value of the function.

Following the function header the definition of variables, types, constants, procedures and functions local to that function is possible. The function body starts with the keyword begin and ends with the keyword end. In between these two keywords, only commands are allowed. All commands have to be seperated by each other using a semicolon. The keyword begin can be abbreviated with a { character, and the keyword end can be abbreviated with a } character.

procedure

A procedure is very similar to a function which does not return a value. Therefore, defining a procedure is also very similar:

procedure <procname> ( <arguments> );

Defining the arguments is the same as with functions. Of course, it is not possible to assign a value to <procname> because there is no return value.

screen

As described above a screen is the same as a procedure without arguments. It gets defined by:

screen <screenname>;

However, a screen cannot be called like a procedure, but only with the menu command.

Data types

There are two general types: scalar and non-scalar. Scalar types are those which can be internally represented by a number. All others including records, arrays and strings are non-scalar. Each possible value of a scalar type has an ordinal number, so scalar types are defined by their range of ordinal numbers.

The following basic types are already predefined:

integer

The integer type is a scalar type with an internal data width of two bytes. It's range of values is from -maxint to +maxint, where maxint is a predefined constant with a value of 32767.

string

The string type is different from Pascal, which only knows about strings of a constant length. However, strings in MGL can have a variable length. Usually, the maximum length for a string is 255 characters. This can be reduced by appending a maximum length in square brackets to the type identifier: string[16] therefore limits the maximum length of a string to 16 characters.

char

The character type is a scalar type with an internal data width of one byte. It's ordinal numbers range from 0 to 255.

boolean

The boolean type is a scalar type with an internal data width of one byte. It's only possible ordinal numbers are 0 and 1.

Note that there is no floating point type in MGL. Like Pascal and unlike C, MGL is very strict regarding types. For example it is not possible to assign an integer value to a character variable.

In addition to these basic types, it is also possible to define additional scalar and non-scalar types. User defined scalar types can be enumerations or subrange types:

Enumeration

The enumeration type consists of named elements, which have to be specified in round brackets. All named elements are seperated by commas from each other, and get assigned an ordinal number starting at zero. The following declaration defines an enumeration type with five elements:

( Mon, Tue, Wed, Thu, Fri )

where Mon gets the ordinal number 0, and Fri gets the ordinal number 4.

Subrange

A subrange type is based upon one of the basic scalar types integer, char and boolean. It is specified by giving two constant values, seperated by three dots. For example, the following declaration defines a subrange type with 26 elements:

'A'...'Z'

This subrange type consists of character elements, ranging from the character 'A' to the character 'Z'. Declaring a variable with this type, and assigning it a value of 'a' will result in a runtime error. Subrange types can be useful for specifying array subscripts.

User defined non-scalar types can be records and arrays:

Record

A record is a collection of elements with possibly different types. It is defined with the keyword record. Following this keyword can appear all elements of the record, with the same syntax as with variable declarations. The record definition is terminated by the end keyword. Unlike Pascal, MGL does not implement variant record elements (which are similar to unions in C).

Array

An array is a collection of elements with the same type. It is defined with the keyword array followed by a scalar type in square brackets, the keyword of and the type specifier of the elements. The following are examples of array definitions:

array [char] of integer

array [10...20] of char

In the first example an array of integers is defined with a number of 256 elements (the ordinal number range of the char type), while in the second example an array of characters is defined with a number of 11 elements. Note that unlike Pascal an array of characters is handled differently than a string type.

Expressions

An expression is a set of operations applied to variables and constants. The operations allowed differ for the types involved in the expression. There is no possible operation for array and record types (which is in contrast to Pascal, where it is possible to compare variables with these types). The following operations are available:

Numerical operations

These include addition ( + ), subtraction ( - ), multiplication ( * ), division ( / or div ) and the modulo operation ( % or mod ). All these operations can only be applied to numerical values (i.e. values of integer type and integer subrange types), and the result has the same type as the types involved in the operation. Also there are two predefined functions which operate on integer values: abs returns the absolute value of it's argument, and sqr returns the square value of it's argument. In addition to these operations it is also possible to apply boolean operations to numerical values: and, or, xor and not. These apply bitwise on the numerical values, and the result is numerical again.

String operations

The only valid string operation is the addition ( + ). This will append the string on the right side of the plus sign at the end of the string on the left side. The result is always a string. If the result gets assigned to a variable, it will be automatically truncated if it exceeds the maximum length allowed for the variable.

There are three special operations with string variables. When the variable name is followed by a dot and the keyword len, the result is a numerical value and specifies the actual length of the string. Similar to arrays, giving a numerical value in square brackets to a variable returns the character at the corresponding string position. It is an error to use a numerical value which is larger than the maximum string size. However, no error will be generated when an index is used which is beyond the actual end of the string, but still within the limits of the string variable. As an addition to this index operation, it is possible to extract a substring of a string variable by specifying two numerical index values, seperated by a comma and surrounded by square brackets.

Character operations

As with strings, characters can be added together. However, the result will always be a string. Similarily, a character can also be added to a string. Another operation with a character is the multiplication. On the left side of the multiplication sign has to be a character value, and on the right side has to be a numerical value. The character gets repeated as often as indicated by this numerical value. Therefore, the result is always a string.

A special character operation is the chr function. It converts a numerical value into a character. In contrast to Pascal, only the lower 8 bits of the numerical value matter, and a runtime error will not occur, if it is larger than the largest ordinal value for characters.

Boolean operations

The normal boolean operations and, or, xor and not can be applied to boolean values. The result is boolean again. Also, comparisons (e.g. =, <>, >, <, >=, <=) always give a boolean result regardless of the types involved in the comparison. It is only possible to compare two values which are assignable to each other. In contrast to Pascal, a comparison of arrays and records is not possible.

A predefined boolean function is odd, which returns true if it's numerical argument is odd, and false if it's argument is even.

General scalar operations

For all scalar values the ord function returns their ordinal values. The result of this function is always numerical. The functions succ and pred increase resp. decrease the corresponding scalar value to the next ordinal value. It will result in a runtime error to increase or decrease out of the range of the corresponding scalar type.

When the result of an expression turns out to be constant, it will be collapsed by the compiler and treated as one single constant value. This is also true for most of the builtin functions like chr or ord. Whenever it is required to use a constant value, for example for defining an integer subrange type or for specifying a maximum length for a string, it is possible to use an expression which consists only of constant values. These can also be named constants like maxint or user defined constants.

Commands

MGL knows of some commands in addition to Pascal to ease the generation of menus. Also some Pascal commands are not implemented, especially for and case. The following commands are implemented in MGL:

while <boolean expression> do

Repeat the following command or block of commands, delimited with begin and end as long as the boolean expression returns true.

repeat <commands> until <boolean expression>

Repeat the commands until the boolean expression returns true. In contrast to while this command does not allow the <commands> block to be delimited by begin and end.

if <boolean expression> then <command 1> else <command 2>

Execute the command 1 if the boolean expression returns true. Otherwise execute the command 2. Both commands can also be command blocks delimited by begin and end. The keyword then is optional and can be left out. With all these commands affecting program flow, the keywords begin and end can be replaced with the characters { and } respectively.

print <expression> at [ <x> , <y> ]

This will print the result of the expression onto the screen at the position pointed to by the two numerical expressions x and y. The position specification is optional. If it's not given, the expression will be printed at the current cursor position. The expression to be printed has to result in a numerical, string or character type.

get <variable> at [ <x>, <y> ] with timeout <expression>

Read a value into the variable using the timeout in seconds given by the numerical expression. If no coordinates are given the current cursor position will be used. The variable has to have a numerical, character or string type. It will be echoed to the console while reading. Note that you will get a runtime error when the user enters a numerical value which is larger than the range for the numerical variable. However, entering values larger than +/- maxint is not possible, so a runtime error will not occur with an integer variable. When no timeout is given the get function will wait forever for proper user input. If a timeout is given, the variable receives the value entered so far when the timeout occurs.

select at [ <x>, <y> ] with timeout <expression> of <items> end

This is a complex command which operates similar to the get command. However, it does only allow entering a numerical character from x and y coordinates and with a timeout given by the numerical expression. Depending on what key the user pressed a specific item is selected. When no key is pressed, the default item is executed. If the user pressed a key for which no item is defined, no action is taken and another key press is waited for. select operates in an endless loop, which can only be left by using the keywords break, return and restart.

An item is defined by using the command

item <constant numerical expression> : <commands>

or

default : <commands>

The constant numerical expression has to have a value from 0 to 9, corresponding to the keys which are accepted by select. There is no limit concerning the number of commands to be executed when the corresponding item is selected.

load <string expression> from <IP address> gateway <IP address>

This command lets the program return to the bootrom and tell the bootrom to load the file named by the string expression from the system given by the first IP address and using a gateway given by the second IP address. The string expression does not have to be constant, but the IP addresses must be constant, because the runtime module presently lacks a hostname resolver. However, the IP addresses can either be specified in dotted format like 111.222.111.222 or as a string constant expression. If it's a string constant, the compiler treats it as a host name and resolves the name into an IP address at compile time.

menu <menu id>

Call another screen with the given name. This is like a procedure call but for screens.

gotoxy at [ <x> , <y> ]

Move the cursor to the position on the screen given by the two numerical expressions x and y.

restart

Continue operation at the beginning of the current screen, procedure or function.

break

Leave the innermost while, repeat or select command and continue execution at the end of the loop.

return

Terminate the current screen, function or procedure and return to the caller.

<variable> := <value>

This will assign a value to a variable. The value has to be assignable to the variable regarding types.

Scope of symbols

All symbols which are not keywords can be redefined in a different nesting level. While it's not possible to have two symbols with the same name within one procedure or function, the same symbol names can be used again in a different procedure, or in a procedure which is local to the current procedure. This also applies to internally defined symbols, which have an implicit nesting level of -1, which is lower than any other level. Therefore you can redefine internal procedures or functions like print or get.

BOOTP values

When the MGL program gets loaded and run by a boot rom, it gets passed the BOOTP record. It is possible to access the BOOTP tags in this record from within the program by using the special variable construct $[ <expression> ] The expression has to be numerical, and defines the number of the tag in the BOOTP RFC vendor data area. The type of this special BOOTP variable is always a string. You can use this kind of variable within string expressions, or assign a value to it like with any other variable. When the MGL program terminates, the so modified BOOTP record gets passed back to the boot rom. Using this method it is possible to pass special BOOTP tags to the operating system loaded next by the boot rom. But note that the boot rom only allows a maximum size of 2048 bytes for the BOOTP record. When this space gets exceeded, the resulting tags are truncated.

To make accessing the BOOTP tags easier, some constants are predefined:

BOOTP_HOSTNAME

Tag 12: The hostname of the client. This tag is only present in the BOOTP record if you specified the hn flag in /etc/bootptab on the server.

BOOTP_DOMAIN

Tag 15: The domain name of the client. This tag is only present if you specified the dn tag in /etc/bootptab on the server.

BOOTP_ROOTPATH

Tag 17: The path to the root directory of the client. This tag is only present if you specified the rp tag in /etc/bootptab on the server.

So, for example to access the client's root directory on the server, you can use $[BOOTP_ROOTPATH] in your MGL program. In addition to this there are also two read-only variables predefined:

hostname

This string variable gives the client's host name. It has the same value as $[BOOTP_HOSTNAME].

servername

This string variable contains the name of the BOOTP server which answered the request of the boot rom. Since this name is not encoded as a tag in the BOOTP record, using this variable is the only way of accessing the server name.

DATABASE FILE

mknbi-mgl does not use any sections in the configuration file except the [general] section. However, for easier maintenance of different client configurations the database file can be used. The entries have to be preceded by a [<system name>:mknbi-mgl] tag and can contain the following parameters:

outfile

Specifies the name of the output file

infile

Specifies the name of the input source file

debug

This boolean parameters controls the generation of debugging code.

noheader

If the boolean argument of this parameter is true, no boot image header will be produced, so that the output file can be run as a COM program under DOS. This is useful for testing.

optimize

This parameter can have the values 86, 186 or 386 and controls the level of optimization.

BUGS

Need to cleanup the code generator. Also, there is almost no optimization, and error handling and reporting has to be better. Since this is alpha code, there are probably still many undiscovered bugs in the compiler.

SEE ALSO

netboot.config(5)

AUTHOR

mknbi-mgl was written by Gero Kuhlmann <[email protected]>.

History

--batch-sys since Debian 2.1 (Slink); --config-file since Debian 2.1 (Slink); --debug since Debian 2.1 (Slink); --help since Debian 2.1 (Slink); --infile since Debian 2.1 (Slink); --noheader since Debian 2.1 (Slink); --opt186 since Debian 2.1 (Slink); --opt386 since Debian 2.1 (Slink); --opt86 since Debian 2.1 (Slink); --outfile since Debian 2.1 (Slink); --verbose since Debian 2.1 (Slink); --version since Debian 2.1 (Slink); -3 since Debian 2.1 (Slink); -C since Debian 2.1 (Slink); -b since Debian 2.1 (Slink); -d since Debian 2.1 (Slink); -h since Debian 2.1 (Slink).

-g until Debian 2.0 (Hamm).