The Pascal Library

# The Pascal Library

In the last chapter, we described the standard library subprograms related to I/O. In this chapter, we will deal with other commonly used subprograms. Note that the HELP facility can also give information on most of these subprograms.

## Conversion Functions

The following functions convert one type of data to another.

#### `round(x)`

This converts a `real` expression `x` into an `integer` value by rounding it up or down to the nearest integer. Therefore, `round(3.2)` has the value `3` while `round(3.7)` has the value `4`. `round(-3.2)` has the value `-3` while `round(-3.7)` has the value `-4`.

#### `trunc(x)`

This converts a `real` expression `x` into an `integer` value by truncating the fractional part. Therefore the expressions `trunc(3.2)` and `trunc(3.7)` both have the value `3`. `trunc(-3.2)` and `trunc(-3.7)` both have the value `-3`.

#### `chr(i)`

This converts an `integer` expression `i` into a `char` value. The integer is converted to whatever character has the same ASCII representation. This means that the integer must lie inside the values for the ASCII character set, in this case `0` to `255`.

#### `ord(c)`

This converts a scalar type expression into an integer. `char` values are translated into the integer that has the same ASCII representation. This means that for any `char` value `c`,

`chr( ord(c) )` equals `c`

The effect of `ord` on enumerated types was described in Chapter 3.

The `ord` function is a non-standard function because its arguments can have any scalar type. For this reason, it cannot be passed as an function argument to another subprogram.

#### `Frac(x)`

This returns a real number whose value is the fractional part of the real value x. For example,

>     n := Frac(7.3);

will cause n to have the value 0.3. This comes from Turbo Pascal.

#### `Int(x)`

This returns a real number whose value is the integer part of the real value x. For example,

>     n := Int(8.3);

will cause n to have the value 8.0. This comes from Turbo Pascal.

#### `Hi(num);`

This routine returns the high-order byte of the integer argument. For example,

>     i := Hi($1234);

would set `i` to be $12. [Found in `TURBOLIB.AP`]

#### `Lo(num);`

This routine returns the low-order byte of the integer argument. For example,

>     i := Lo($1234);

would set `i` to be $34. {TLIB{

#### `Swap(variable);`

This function returns an integer whose high and low bytes are reversed from those of the `variable` argument. For example,

>     i := Swap($1234);

will result in `i` having the value $3412. [Found in `TURBOLIB.AP`]

## Mathematical Functions

Pascal supports most of the basic mathematical functions.

#### `abs(x)`

returns the absolute value of `x`. `x` may be `real` or `integer`, and the result will have the same type as the argument.

#### `odd(x)`

returns `true` if `x` is an odd integer, and `false` if `x` is even. `x` must be an integer.

#### `sqr(x)`

returns the square of `x`. `x` may be `real` or `integer`, and the result will have the same type as the argument.

#### `sqrt(x)`

returns the square root of `x`. `x` may be `real` or `integer`. The result is always `real`.

#### `sin(x)`

returns the sine of `x`. `x` may be `real` or `integer`, and gives an angle in radians. The result is always `real`.

#### `cos(x)`

returns the cosine of `x`. `x` may be `real` or `integer`, and gives an angle in radians. The result is always `real`.

#### `arctan(x)`

returns the arctangent of `x`. `x` may be `real` or `integer`. The result is always `real`, and gives an angle in radians.

#### `ln(x)`

returns the natural logarithm of `x` (base `e`). `x` may be `real` or `integer`. The result is always `real`.

#### `exp(x)`

returns the number `e` to the power `x`. `x` may be `real` or `integer`. The result is always `real`.

The functions `abs` and `sqr` are non-standard, in that they may return either an integer or real number. For this reason, they may not be passed as function arguments to subprograms.

## Order Functions

Order functions can be applied to any scalar type. The ``successor'' function `succ` has the form

>     next := succ(value);

where `value` is any scalar value. The result returned by `succ` is the next value in the scalar type. For example, if `value` is an integer, `succ` returns the next integer, so

>     succ(3) = 4

If `value` is an enumerated type, `succ` returns the next value in the enumerated list. Thus if you define

>     type
>         days = (sun,mon,tue,wed,thu,fri,sat);

you would have

>     succ(sun) = mon
>     succ(mon) = tue
>     { And so on }

If you try

>     succ(sat)

you will be given an error message. `sat` is the last element in the enumerated type and has no successor.

The ``predecessor'' function `pred` is the converse of `succ`. It has the form

>     previous := pred(value);

`pred` returns whatever comes before the given `value`. For example,

>     succ(3) = 2
>     succ(tue) = mon

If `value` has no predecessor, Alice will give you an error.

## Packing Functions

Other implementations of Pascal have functions named `pack` and `unpack`. `pack` takes the values that are stored in a normal array and assigns them one by one to a packed array. `unpack` takes the values that are stored in a packed array and assigns them one by one to a normal array.

Alice recognizes the names `pack` and `unpack`, but the subprograms just give an error if called.

## Dynamic Memory Allocation

The `new` procedure can be used to allocate memory dynamically at execution time. The memory is allocated from a store of memory called the heap. The simplest form of `new` is

>     new(p);

where `p` is a pointer variable. The procedure will allocate sufficient memory space to contain the sort of data that `p` points to. For example, if `p` is declared

>     var
>         p : ^integer;

`new` will obtain enough space to hold an integer. If `p` is a pointer to some record type, `new` will allocate enough space to hold a record of that type. When the space has been obtained, `new` will set up `p` to point at the space that has been obtained.

As a simple example, the following code uses `new` to set up a linked list of records.

>     type
>         listptr = ^listelement;
>         listelement = record
>             link : listptr;
>             other : {Various things};
>             end;
>     var
>         root, pl : listptr;
>     begin
>     {Get root of list}
>     new(root);
>     pl := root;
>     for i := 1 to 10 do begin
>         new(pl^.link);
>         pl := pl^.link;
>         end;
>     pl^.link := nil;
>     {And so on}

This allocates `10` new list elements and links them together in a linked list.

Note that `new` is one of the few ways to initialize a pointer. All the other ways involve calls to Pointer functions (described later in this chapter).

A second form of `new` is used when allocating space for records with variant fields. This has the form

>     new(p,t1,t2,...);

where `p` is a pointer to the record type, and `t1,t2,...` are values for one or more tag fields of the variant part. By plugging these values into the various tag-fields, `new` can determine the correct amount of memory to allocate. Note that `new` does not put these values into the tag-fields of the record it creates; it simply uses these values to determine how big the record should be. If `new` is not supplied with enough tag-fields to totally determine the size of memory required, it will allocate enough memory to hold the largest possible record of this type. Alice ignores the tag fields and always does this.

The `dispose` procedure is used to release memory that has been allocated through `new`. `dispose` should be called with the same arguments that were passed to the `new` procedure that allocated the memory. Therefore you should use

>     dispose(p);
>     dispose(p,t1,t2,...);

This is the only way that memory allocated by `new` may be freed up. In particular, memory allocated inside a subprogram is not automatically released when the subprogram terminates. You must release the memory explicitly with `dispose`. Releasing the memory makes it available for re-allocation in later calls to `new`, but it does not decrease the size of your program.

If you call `dispose` and give a file variable as an argument, Alice will close the file associated with the variable.

The `Mark` procedure is used to find out the current value of the heap pointer. The usage is

>     Mark(variable);

where the `variable` is a pointer.

The `Release` procedure sets the heap pointer back to a particular value. Its usage is

>     Release(variable);

where the `variable` is a pointer.

Both `Mark` and `Release` are found in `TURBOLIB.AP`. They are intended for Turbo Pascal compatibility, and should not be used in new programs; instead, `Dispose` should be used. Also note that `Mark` and `Release` are not compatible with the `New`/`Dispose` form of memory allocation.

To find out how large a block can be allocated, use the `MaxAvail` function. It returns the size of the largest block of contiguous free storage available on the heap.

To find out the total amount of free memory, use the `MemAvail` function. It returns the total amount of storage available on the heap.

Both `MaxAvail` and `MemAvail` return an integer specifying the number of 16-byte units called ``paragraphs.'' The returned value is an integer; if it is less than zero, the actual value can be computed as a real number using the expression MaxAvail + 65536.0, or MemAvail + 65536.0. In the current release of Alice, the values will always be greater than zero.

The `GetMem` and `FreeMem` routines are used to allocate and deallocate dynamic memory blocks of a user-specified size.

>     GetMem(variable, n);

will allocate `n` bytes from the heap and set the pointer `variable` to point to that block. This is more general than `new`, since it is not limited to allocating just enough space for a particular instance of a variable.

The `MaxAvail` function can be used to find out in advance if the call to GetMem will succeed or fail.

>     FreeMem(variable, n);

will return the memory pointed to by `variable` to the free pool for subsequent re-allocation. The pointer should be one set by `GetMem`, and the value of `n` should match the value in the call to `GetMem`. It is important that you not call `FreeMem` on any pointer not allocated by `GetMem`, and that you not accidentally free the same memory twice. You should also be careful not to keep copies of the pointer from `GetMem` in other pointer variables, and then try and refer through them after the memory has been deallocated by `FreeMem`.

## String Manipulation Routines

String manipulation routines manipulate string constants and variables with string types. Our descriptions of these routines will give examples of source code using each routine, followed by output from this source code.

The first set of functions in this section are taken from Watcom Pascal.

#### `StrConcat(str1,str2);`

This procedure concatenates the contents of `str2` on the end of `str1`.

>     str1 := 'hello';
>     str2 := ' there';
>     StrConcat(str1,str2);
>     writeln(str1);
>     - - - - -
>     hello there

Note the string concatenation can also be performed with the ``+'' operator.

#### `StrDelete(string,N,position);`

This procedure deletes `N` characters from `string`. `Position` is an integer giving the subscript of the first character to be deleted.

>     str := 'The quick brown fox';
>     StrDelete(str,2,5);
>     writeln(str);
>     - - - - -
>     The ick brown fox

This routine is similar to the Turbo Pascal `Delete` routine.

#### `StrInsert(str1,str2,N);`

This procedure inserts the contents of `str2` into `str1`. The first character of `str2` will become character N of `str1`.

>     str := 'The quick fox';
>     StrInsert(str1,'brown',5);
>     writeln(str);
>     - - - - -
>     The brownquick fox

This routine is similar to the Turbo Pascal `Insert` routine.

#### `i := StrLen(string);`

This function returns an integer giving the length of a string. For a string constant, this is the number of characters in the constant. For a string variable, it is the number of characters currently stored in the variable. If the variable is not full, it will be the number of characters up to but not including the `StrEnd` that follows the last character in the string.

>     str := 'abcdef';
>     writeln(StrLen(str));
>     str := 'abc';
>     writeln(StrLen(str));
>     writeln(StrLen('xyz');
>     - - - - -
>     6
>     3
>     3

This routine is similar to the Turbo Pascal `Length` routine.

#### `i := StrScan(str1,str2);`

This function looks for the string `str2` inside `str1`. If it is found, `StrScan` returns the subscript where `str2` begins in `str1`. If the string is not found, `StrScan` returns 0.

>     i := StrScan('hello there','the');
>     writeln(i);
>     - - - - -
>     7

This routine is similar to the Turbo Pascal `Pos` routine.

#### `i := StrSize(string);`

This function returns an integer giving the maximum length of a string. If the string is a string constant, this is the number of characters in the string. If the string is a string variable, this is the maximum number of characters that the string can hold, regardless of how many it holds at the moment.

>     var
>         str : packed array [1..10] of char;
>       ...
>     writeln(StrSize('abc'));
>     str := 'abc';
>     writeln(StrSize(str));
>     - - - - -
>     3
>     10

#### `SubStr(str1,N,position,str2);`

This procedure obtains a copy of part of `str1`. `N` is the length of the substring and `position` is the subscript where the substring starts. The substring that is obtained is copied into `str2`.

>     str1 := 'Hello there';
>     SubStr(str1,2,3,str2);
>     writeln(str2);
>     - - - - -
>     ll

Also of note for this purpose is the Turbo Pascal `Copy` function.

The remaining functions in this section are taken from Turbo Pascal.

#### `Delete(str, pos, num);`

This procedure deletes a substring of length `num` starting at position `pos` in the string variable `str`. The remaining characters (if any) in the string are shifted left. This routine is similar to the Watcom StrDelete routine.

#### `Insert(src, dest, pos);`

This procedure inserts the string `src` into the string `dest` at position `pos`. The variables `src` and `dest` are both of type string; pos is of type integer. An error message will be issued if the `pos` is past the end of the string, or the string becomes too long. This is stricter error checking than that provided by the same routine in the Turbo Pascal compiler. This routine is similar to the Watcom StrInsert routine.

#### `Copy(str, pos, num);`

This function returns a substring of the string variable `str` of length `num` starting at position `pos`. It returns the null string (i.e. a string of length zero) if pos is greater than the length of the string.

#### `Length(str)`

This function returns the length of the string argument in characters. For a string constant, this is the number of characters in the constant. For a string variable, it is the number of characters currently stored in the variable. Length gets its value from the special length byte kept at the zeroth index of every string. It is similar to the Watcom Strlen function.

#### `Pos(srch, str);`

This function returns the position in the string `str` at which the string `srch` is found. It returns 0 if `srch` was not found in `str`. For example,

>     i := Pos('exec', 'autoexec.bat');

will cause i to have the value 5, while

>     i := Pos('gremlin', 'halloween');

will cause i to have the value 0. This is similar to `StrScan`

#### `Str(val, str)`

This procedure puts the value of the given variable `val` (which may be integer or real) into the given string variable `str` as an ASCII representation of the number. The variable `val` can actually have format specifiers of the same type found in `write` procedure calls. One can specify a field width for integers and reals. Reals may also have a precision field, just as with `write`. In general

>     Str(i:n, dest);
>     writeln(dest);

is the same as:

>     writeln(i : n);

if `dest` is a string large enough to hold the written number.

Note that Alice places a space in front of integer operands if no field width is provided. A field width of zero eliminates this. This space is not output when Borland deviations (+b) are enabled.

An error occurs if the string is not large enough to hold the number.

#### `Val(str, variable, code);`

This procedure is the inverse of the `str` procedure; it examines the string contained in `str` and converts it to a number which it stores in `var`. The type of the number is either real or integer, depending on the type of `var`. Leading and trailing spaces are not permitted; the valid formats for the number are the same as those for constants in Alice Pascal. In addition, real numbers with nothing before the decimal point (e.g. ".1") are acceptable.

If the conversion is successful, the integer variable `code` is set to zero; otherwise, it contains an index into `str` of the character on which the conversion failed. If this is the case, the value of `var` is undefined.

For example,

>     Val('7.8', n, stat);

will set `n` to 7.8 and `stat` to zero, while

>     Val('7y2', n, stat);

will result in `stat` being 2 (the index of the letter y in the string) and the value of `n` will be undefined.

#### `UpCase(c);`

This function returns a character which is the upper-case equivalent of the character `c`. For example,

>     ch := UpCase('b');

will result in `ch` having the value 'B'. This routine is handy if you want to accept user input in either upper or lower case.

## Pointer Functions

There are a variety of routines that deal with addresses and pointers. Most of these use values of the `Pointer` type, the special Alice built in type that is assignment compatible with all other pointers.

#### `i := address(variable);`

This function obtains the actual memory address of its argument. The argument must be a variable name, a subscripted variable, a field inside a record variable, or an indirectly-referenced variable. `address` returns an integer that gives the machine address that has been allocated to that variable. If a machine address does not fit in an integer, as is the case with the 8086 processor in the IBM-PC, the result returned is actually only part of the address, namely the offset. For example,

>     i := address(v);

obtains the integer offset of the variable `v`.

You can also specify a second argument for `address`, indicating whether you want the segment containing the variable or the offset of the variable within the segment. If the argument is the integer `0`, you get the offset; if it is any other integer, you get the segment. If this second argument is omitted, `address` returns the offset. For example,

>     i := address(v,1);

obtains the segment that contains the variable `v`.

`address` is unique to Alice.

#### `i := Ofs(variable);`

This function is similar to `address`, except that it does not take a second argument and always returns the offset. [Found in `TURBOLIB.AP`]

#### `p := MakePointer(variable);`

This function is much like `address` except that it returns a `Pointer` type value instead of an integer. This can be assigned to any valid Pascal pointer type. `MakePointer` is unique to Alice. Unlike `address`, `MakePointer` does not need to deal with offsets and segments, as a variable of `Pointer` type always holds a pointer. Note, however, that in SMALL model Alice, such pointers may only point within Alice's 64K data segment.

#### `p := Addr(variable);`

This is Turbo Pascal's version of `Makepointer`. It returns a pointer to the given variable. For example,

>     var
>         i : ^integer;
>      ...
>     i := Addr(v);
>     i^ := 10

Places 10 (indirectly) into `v`, assuming it is an integer variable.

[Found in `TURBOLIB.AP`]

#### `p := RawPointer(offset,segment);`

This function converts an integer address into a Pointer type. As such, it is the inverse of `address`. The `offset` argument is an integer giving an offset within a segment, and the optional `segment` argument is an integer giving a segment number. If not provided, the second argument is assumed to be the Data Segment. In small-model Alice, this segment number must either be omitted or equal to the Data Segment (Dseg). The value returned by `RawPointer` has the `Pointer` type. `RawPointer` is unique to Alice.

#### `p := Ptr(segment, offset);`

This function creates a pointer from two integers, and is the Turbo Pascal equivalent of `RawPointer`. None of the parameters are optional, and in SMALL model Alice, the first parameter must be equal to the Data Segment as returned by `DSeg`.

#### `i := peek(address);`

This function returns the value that is at the given memory address. The `address` argument is given as an integer. The value that is returned is a single byte and will therefore be in the range 0..255. On the IBM PC, only the first 64K of physical memory may be accessed this way. This 64K is not the Data Segment of Alice. To access other addresses, use `Mem_To_Var` (described below).

#### `poke(address,value);`

This procedure stores a value in the given address. The `address` is given as an integer. Since `poke` only stores a single byte, the `value` argument should be in the range 0..255. If it is not, `poke` will use `value mod 256`. On the IBM PC, only the first 64K of physical memory may be accessed this way. This is not the Data Segment used by Alice. To access other addresses, use `Var_To_Mem` (described below).

#### `Var_To_Mem(variable,offset,segment)`

This routine copies a variable to an absolute address in memory. On an IBM-PC, this address is specified with two integers, an offset and a segment. Great care should be taken when using this routine, as writing improperly on absolute memory can easily crash your machine. The segment argument is optional, and defaults to the Data Segment. Be warned when writing into the Data Segment that this routine does not mark the memory it touches as initialized.

#### `Mem_To_Var`

This routine copies from absolute memory into the specified variable. It is the inverse of `Var_To_Mem`. On an IBM-PC, the address is specified with two integers providing the offset and segment of the desired memory location. The number of bytes copied equals the size of the variable in question.

#### `Move(src, dest, count)`

This procedure copies a number of bytes specified by the integer `count` from the location of the variable specified by `src` to the location of the variable specified by `dest`. Both `src` and `dest` may be of any type. It is up to you to make sure there is enough room in `dest` to contain the data. See also `Var To Mem` and `Mem To Var`. This routine is contained in `TURBOLIB.AP`.

#### `FillChar(variable, num, value)`

This procedure fills memory with the given character `value`, starting at the base address of the specified `variable`, for a length of `num` characters. The `var` can be of any type; `num` must be an integer, and the `value` must be a `character` or `byte`.

This routine is very fast (much faster than doing it yourself in a loop). It is up to you to make sure that there is enough room in the variable to contain the data.

Note that strings have a length byte in the zeroth position; this byte will get overwritten if you pass a string to `FillChar`.

This routine is contained in `TURBOLIB.AP`.

#### `i := Dseg;`

This function returns the value of the Data Segment as an integer. In small model Alice, this is the segment in which all variables are found. When constructing pointers (for example, using `ptr`), you should use the value of `dseg`. This routine is contained in `TURBOLIB.AP`.

#### `i := Cseg;`

This function returns the value of the Code Segment as an integer. This value should almost never be used for anything, since Alice is an interpreter. (For more information about the differences between interpreters and compilers, refer to the section on converting between Turbo and Alice). The Cseg function returns the value of one of the code segments of the Alice interpreter. Any offsets into the Cseg that the program uses will be meaningless; also, using Cseg in the setting of interrupt vectors is incorrect. This routine is contained in `TURBOLIB.AP`.

#### `i := Sseg;`

This function returns the value of the Stack Segment as an integer. In small model Alice, this is the same as the Data Segment. This routine is contained in `TURBOLIB.AP`.

#### `i := Seg(variable);`

This function returns the segment address of the given variable. In small model Alice, this will always be the same as the program's data segment (i.e., `dseg`). This routine is found in `TURBOLIB.AP`.

## Random Number Generation

Alice includes a function called `random` that generates pseudo-random numbers. Unfortunately, maintaining compatiblity with both Watcom and Turbo Pascals is impossible with this function, so two versions exist. When Turbo Pascal features are disabled with the ```-t`'' option, `random` acts as it does in Alice 1.2 and Watcom Pascal. Otherwise it acts as the `random` function of Turbo Pascal.

For both systems,

>     initrandom(seed,upper);

initializes a pseudo-random number generator. Both `seed` and `upper` are integers. Different values of `seed` will give different number sequences; identical values of `seed` will give identical sequences. When using Alice 1.2/Watcom random numbers, these will be generated in the range `0..(upper-1)`.

The actual random numbers are obtained from a function named `random`. If you disable Turbo Pascal compatibility by invoking ALICE with the `-t` option, the call to `random` has the form

>     i := random;

This returns a pseudo-random integer. You may not call `random` until you have initialized the random number generator with `initrandom`.

If you do not disable Turbo Pascal features, the call to `random` has the form

>     i := random(N);

where `N` is an integer. This generates a pseudo-random integer in the range `0..N-1`. In this case, you do not have to call `initrandom` first. If you omit the `N` as in

>     a := random;

the function will return a pseudo-random `real` number that is strictly less than `1.0` and greater than or equal to `0.0`.

#### `Randomize;`

This procedure re-seeds the random number generator from the system clock, so that a different sequence of values will be produced for every run. [Found in `TURBOLIB.AP`]

### System Calls

The following routines let you perform system calls. Users should normally never need to use these.

#### `i := SysFunc(p,arg,arg,arg,...);`

This function calls a system function. The `p` argument is a `Pointer` type pointing to the memory location of the function to be called. The function is passed (in Microsoft C calling style) an itneger argument count and pointers to the top and bottom of a vector of arguments. The value returned by `SysFunc` is the value returned by the system function. `SysFunc` comes from Watcom Pascal, but is not compatible with it on the IBM-PC.

#### `SysProc(p,arg,arg,arg,...);`

This procedure works the same way as `SysFunc`. It is used to call system routines that do not return a value. `SysProc` comes from Watcom Pascal, but is not compatible with it on the IBM-PC.

### Internal C Routines

The following routines all make calls to a special internal library of useful routines contained in Alice. These routines are not intended for general users, and no guarantees are made they they will remain the same from one Alice release to the next. The Alice distribution disks come with Pascal library routines that make use of these internal features. To be safe, make use of the libraries instead of calling these ``CProcs'' directly. These routines are all unique to Alice.

#### `i := CIntFunc(args);`

calls a C function that returns an integer. For more information, see the Alice User Guide.

#### `l := CLongFunc(args);`

calls a C function that returns a long integer. `CLongFunc` actually returns a pointer to this long integer. The long integer is expressed as a record with two integer fields named `low` and `high`. These contain the two halves of the long integer. For more information, see the Alice User Guide.

#### `p := CPtrFunc(args);`

calls a C function that returns a pointer. For more information, see the Alice User Guide.

#### `CProc(args);`

calls a C function that does not returns a value. For more information, see the Alice User Guide.

#### `p := SysPointer(i);`

returns the value of an Alice control variable. For more information, see the Alice User Guide. `SysPointer` is unique to Alice.

### Outside World Interface

#### `Chain(filename);`

runs the Alice program contained in the specified file. There is a similar routine in Turbo Pascal; however, it is not the same. Turbo's Chain routine only runs special Turbo chain files, and preserves variables from one program to the other. In Alice, the `chain` routine will erase the current program, and load and run the new one. This is found in the Useful DOS routines library.

#### `Execute(filename);`

This routine executes a DOS program in the same way that the Alice `DOS` command does. The command is executed, and control is returned to the running Alice program when it terminates. The ``COMMAND.COM'' shell program is not used, so commands built into it like ``DIR'' and ``COPY'' may not be used directly. Note that sufficient memory must be available to execute the command at the same time as Alice. This is found in the Useful DOS routines library.

## File and Directory Handling Routines

#### `ChDir(path);`

This routine changes the DOS current directory to the specified path. If the string includes a leading drive specifier, the current drive is changed as well. For example,

>     ChDir('c:\data');

will set the current drive to be C:, and the current directory to be the \data directory on that drive.

If you change directories, any relative pathnames (i.e. pathnames that are not completely specified from the root directory down) will no longer be valid. In particular, this applies to the save file name. This routine is contained in `TURBOLIB.AP`.

#### `Erase(filename);`

This procedure erases the specified file from the disk. It is important that you do not erase a file that is currently active (that is, opened using `reset` or `rewrite` or `append`). This routine is contained in `TURBOLIB.AP`.

#### `GetDir(drive, stringvar);`

This procedure stores into the specified string variable the current directory for the specified drive. The drive is an integer, 1 for A:, 2 for B: and so on. If drive is 0, the current drive is assumed. For example,

>     GetDir(3, currdir);

will store into the string variable currdir the current directory for drive C:. A typical directory string would be '\data\first'. If the current directory for the specified drive is the root directory, the string variable will contain the null string (i.e. StrLen(currdir) will be zero).

This routine is contained in `TURBOLIB.AP`.

#### `MkDir(path);`

This procedure creates a new directory on the disk, whose name is contained in the string argument `path`. This new directory is always created on the current drive. For more information on directories, see the DOS manual.

#### `RmDir(path);`

This procedure removes (i.e., erases) a directory from the disk. You cannot remove a directory that contains files or other directories. Refer to the DOS manuals for more information about directory handling. This routine is contained in `TURBOLIB.AP`.

#### `Rename(oldpath, newname);`

This routine changes the name of a file or directory on disk. `oldpath` specifies the name (and optional path) of the file or directory to be renamed, and `newname` contains the new name. This routine is contained in `TURBOLIB.AP`.

## Miscellaneous Routines

#### `pause;`

When Alice has ``Debug On'' and it encounters a call to the `pause` procedure, the effect is the same as if a breakpoint was triggered while running the program. The program will stop and wait for run-time commands (e.g. going into immediate mode). When Alice has ``Debug Off'', the `pause` procedure has no effect. If you give `pause` an argument (as in `pause(1)`), the program will pause regardless of whether Debug is On or Off. `pause` comes from Watcom Pascal.

#### `i := SizeOf(variable);`

This function returns the number of bytes of storage that are allocated to a particular variable. With string variables, `SizeOf` is equivalent to `StrSize + 2`. `SizeOf` is found both in Alice and Turbo Pascal, but the Alice version does not allow a type as an argument (i.e. SizeOf(integer) is invalid).

#### `Delay(n)`

This procedure simply delays for `n` milliseconds, where n is an integer. The system clock "ticks" every 55 milliseconds, and this limits the accuracy of `delay`. Note that the system clocks on all members of the PC family run at the same speed, so a call to `delay` will produce a consistent delay regardless of whether it's running on a PC, an XT or an AT.

You can use the break key to stop the delay and interrupt the execution of your program.

#### `Halt(n);`

The `halt` routine will terminate your program and return to Alice. The argument is optional; in the Turbo Pascal version of this routine, the argument is the "return value" or "error level" or "exit status" of the program that is passed back to DOS.

#### `Intr(intnum, regrec);`

This procedure generates a software interrupt of type `intnum` (an integer) and transfers register values into and out of the interrupt routine using the record `regrec`. The variable `regrec` should be declared as follows:

>     type regs = record
>             AX,BX,CX,DX,BP,SI,DI,DS,ES,Flags : Integer;
>          end;
>     var
>          regrec : regs;

The various register fields should be set to whatever values the registers ought to contain at the time the software interrupt is issued; when the `Intr` procedure returns, the various fields of the record will hold the register contents as of the completion of the interrupt.

The `Flags` field may be read from, but its value is not used to change the actual hardware flags register.

It is important that the full record be used, not a shorter version of it, since the routine that copies the hardware registers into the `regrec` record assume the full record is available.

Use of `Intr` should be avoided, since there are higher-level functions for almost everything.

#### `MsDos(regrec);`

This procedure executes the software interrupt that interfaces to the MSDOS operating system. It is equivalent to `Intr($21, regrec)`. It transfers register values into and out of the MSDOS call using the record `regrec`. The variable `regrec` should be declared as follows:

>     type regs = record
>             AX,BX,CX,DX,BP,SI,DI,DS,ES,Flags : Integer;
>          end;
>     var regrec : regs;

The various register fields should be set to whatever values the registers ought to contain at the time that MSDOS is called; when the `MsDos` procedure returns, the various fields of the record will hold the register contents as of the completion of the call to MSDOS.

The `Flags` field may be read, but its value is not used to change the actual hardware flags register.

It is important that the full record be used, not a shorter version of it, since the routine that copies the hardware registers into the `regrec` record assume the full record is available.

#### `TPLibProc(filename, offset);`

#### `TPLibFunc(filename, offset);`

These procedures load a binary file from the specified `filename` (a string constant or constant expression) and execute the routine at the specified (integer) `offset` into that file.

The machine language code contained in the binary file must be position independent.

Some Turbo Pascal programs (notably those written for version 2.0 of Turbo Pascal) assume only one routine per binary file; in this case, use an offset value of zero.

On entry to a routine called in this way, the return address is on top of the stack, and it is always a one-word value (i.e. it is a return address within the current code segment). The routine ends with a RET instruction, which also removes the parameters from the stack. The BP register should be preserved (usually by pushing it, and then transferring the SP register into BP to allow easy access to variables in the stack frame). Restoring SP from BP and then popping BP is a standard way of ending a function.

If the routine has additional parameters following it in its call, then the following applies:

- `Var` type parameters are passed as long pointers (segment address is higher on the stack than the offset). - value parameters generally have their actual values pushed onto the stack - integers, chars, bytes, subranges of integers and booleans will take a single word, with the top byte set to zero for chars and bytes; - real values are stored as six-byte quantities, with the exponent lowest in memory. - strings are stored as the string length, followed by the characters in the string in reverse (i.e. right-to-left) order up into the stack. - sets always occupy 32 bytes (one bit for each member). - pointers are passed much like `var` parameters, with the word containing the segment address higher than the offset. - arrays and records are passed like pointers and `var` parameters.

For functions, the function value is returned in AX for functions returning scalar types other than reals, DX:AX for functions returning pointers, and on the top of the stack for all others.

Values passed back on the top of the stack are in the same form as parameters to the function (e.g. reals are six bytes, exponent lowest in memory).

These routines are found in `TURBOLIB.AP`.