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.