Turbo Pascal Reference Manual Version 3.0

Chapter 22
CP/M-80


This chapter describes features of TURBO Pascal specific to the 8-bit CP/M-80 implementation. It presents two kinds of information:

  1. Things you should know to make efficient use of TURBO Pascal. Pages 259 through 272.
     
  2. The rest of the chapter describes things which are only of interest to ex-perienced programmers, such as machine language routines, technical aspects of the compiler, etc.

eXecute Command

You will find an additional command on the main TURBO menu in the CP/M-80 version: eXecute. It lets you run other programs from within TURBO Pascal, for example copying programs, word processors - in fact anything that you can run from your operating system. When enter-ing X, you are prompted:

Command:

You may now enter the name of any program which will then load and run normally. Upon exit from the program, control is re-transferred to TURBO Pascal, and you return to the TURBO prompt > .

compiler Options

The O command selects the following menu on which you may view and change some default values of the compiler. It also provides a helpful function to find runtime errors in programs compiled into object code files.

CP/M-80 259

compiler Options

compile -> Memory
           Com-file
           cHn-file

command line Parameter:

Find run-time error Quit

Figure 22-1: Options Menu

Memory / Com file / cHn-file

The three commands M, C, and H select the compiler mode, i.e. where to put the code which results from the compilation.

Memory is the default mode. When active, code is produced in memory and resides there ready to be activated by a Run command.

Com-file is selected by pressing C. The arrow moves to point to this line. When active, code is written to a file with the same name as the Work file (or Main file, if specified) and the file type .COM. This file contains the program code and Pascal runtime library, and may be activated by typing its name at the console. Programs compiled this way may be larger than programs compiled in memory, as the program code itself does not take up memory during compilation, and as program code starts at a lower address.

cHain-file is selected by pressing H. The arrow moves to point to this line. When active, code is written to a file with the same name as the Work file (or Main file, if specified) and the file type .CHN. This file contains the program code but no Pascal library and must be activated from another TURBO Pascal program with the Chain procedure (see page 263).

When Com or cHn mode is selected, the menu is expanded with the following two lines:

260 TURBO Pascal Reference Manual

compiler Options

Start address: XXXX (min YYYY)
End address: XXXX (max YYYY)

Figure 22-2: Start and End Addresses

Start Address

The Start address specifies the address (in hexadecimal) of the first byte of the code. This is normally the end address of the Pascal library plus one, but may be changed to a higher address if you want to set space aside e.g. for absolute variables to be shared by a series of chained pro-grams.

When you enter an S, you are prompted to enter a new Start address. If you just hit < RETURN >, the minimum value is assumed. Don’t set the Start address to anything less than the minimum value, as the code will then overwrite part of the Pascal library.

End Address

The End address specifies the highest address available to the program (in hexadecimal). The value in parentheses indicate the top of the TPA on your computer, i.e. BDOS minus one. The default setting is 700 to 1000 bytes less to allow space for the loader which resides just below BDOS when executing programs from TURBO.

If compiled programs are to run in a different environment, the End ad-dress may be changed to suit the TPA size of that system. If you antici-pate your programs to run on a range of different computers, it will be wise to set this value relatively low, e.g. C100 (48K), or even A100 (40K) if the program is to run under MP/M.

CP/M-80 261

compiler Options

When you enter an E, you are prompted to enter a End address. If you just hit < RETURN >, the default value is assumed (i.e. top of TPA less 700 to 1000 bytes). If you set the End address higher than this, the resulting programs cannot be executed from TURBO, as they will overwrite the TURBO loader; and if you set it higher than the TPA top, the resulting pro-grams will overwrite part of BDOS if run on your machine.

Command Line Parameters

The P-command lets you enter one or more parameters which are passed to your program when running it in

Memory mode, just as if they had been entered on the DOS command line. These parameters may be accessed through the ParamCount and ParamStr functions.

Find Runtime Error

When you run a program compiled in memory, and a runtime error occurs the editor is invoked, and the error is automatically pointed out. This of course, is not possible if the program is in a .COM file or an .CHN file. Run time errors then print out the error code and the value of the program counter at the time of the error, e.g.:

Run-time error 01, PC=1B56
Program aborted

Figure 22-3: Run-time Error Message

To find the place in the source text where the error occurred, enter the F command on the Options menu. When prompted for the address enter the address given by the error message:

Enter PC: 1B56

Figure 22-4: Find Run-time Error

The place in the source text is now found and pointed out exactly as if the error had occurred while running the program in memory.

262 TURBO Pascal Reference Manual

Standard Identifiers

Standard Identifiers

The following standard identifiers are unique to the CP/M-80 implemen-tation:

Bios Bdos RecurPtr
BiosHL BdosHL StackPtr

Chain and Execute

TURBO Pascal provides two standard procedures: Chain and Execute which allow you to activate other programs from a TURBO program. The syntax of these procedure calls is:

Chain(FilVar)
Execute(FilVar)

where FilVar is a file variable of any type, previously assigned to a disk file with the standard procedure Assign. If the file exists, it is loaded into memory and executed.

The Chain procedure is used only to activate special TURBO Pascal .CHN files, i.e. files compiled with the cHn-file option selected on the Options menu (see page 260). Such a file contains only program code; no Pascal library. It is loaded into memory and executed at the start ad-dress of the current program, i.e. the address specified when the current program was compiled. It then uses the Pascal library already present in memory. Thus, the current program and the chained program must use the same start address.

The Execute procedure may be used to execute any .COM file, i.e. any file containing executable code. This could be a file created by TURBO Pascal with the Com-option selected on the Options menu (see page 260). The file is loaded and executed at address $100, as specified by the CP/M standard.

If the disk file does not exist, an I/O error occurs. This error is treated as described on page 116. If the I compiler. directive is passive ({ $I-}), pro-gram execution continues with the statement following the failed Chain or Execute statement, and the IOresult function must be called prior to further I/0.

CP/M-80 263

Chain and Execute

Data can be transferred from the current program to the chained pro-, gram either by shared global variables or by absolute address variables.

To ensure overlapping, shared global variables should be declared as, the very first variables in both programs, and they must be listed in the same order in both declarations. Furthermore, both programs must be compiled to the same memory size (see page 261). When these condi-tions are satisfied, the variables will be placed at the same address in memory by both programs, and as TURBO Pascal does not automatical-ly initialize its variables, they may be shared.

Example: Program MAIN.COM:

program Main;
var
  Txt:      string[80];
  CntPrg:   file;
begin
  Write('Enter any text: '); Readln(Txt);
  Assign(CntPrg, 'ChrCount.chn');
  Chain(CntPrg);
end.

Program CHRCOUNT.CHN:

program ChrCount;
var
  Txt:      string[80];
  NoOfChar,
  NoOfUpc,
  I:        Integer;
begin
  NoOfUpc :  0;
  NoOfChar := Length(Txt);
  for I :  1 to length(Txt) do
    if Txt[I] in ['A'..'Z'] then NoOfUpc := Succ(NoOfUpc)
  Write('No of characters in entry: ',NoOfChar);
  Writeln('. No of upper case characters: ', NoOfUpc,'.
end.

264 TURBO Pascal Reference Manual

Chain and Execute

If you want a TURBO program to determine whether it was invoked by eXecute or directly from the DOS command line, you should use an ab-solute variable at address $80. This is the command line length byte, and when a program is called from CP/M, it contains a value between 0 and 127. When eXecuting a program, therefore, the calling program should set this variable to something higher than 127. When you then check the variable in the called program, a value between 0 and 127 in-dicates that the program was called from CP/M, a higher value that it was called from another TURBO program.

Note that neither Chain nor Execute can be used in direct mode, i.e. from a program run with the compiler options switch in position Memory (page 260).

Overlays

During execution, the system normally expects to find its overlay files on the logged drive. The OvrDrive procedure may be used to change this default value.

OvrDrive Procedure

Syntax: OvrDrive(Drive)

where Drive is an integer expression specifying a drive (0 = logged drive, 1 = A:, 2 = B:, etc.). On subsequent calls to overlay files, the files will be expected on the specified drive. Once an overlay file has been opened on one drive, future calls to the same file will look on the same drive.

Example:

program OvrTest;

overlay procedure ProcA; 
begin
  Writeln( 'Overlay A' ); 
end;

overlay procedure ProcB; 
begin
  Writeln( 'Overlay B' ); 
end;

CP/M-80 265

Overlays

procedure Dummy;
begin
  {Dummy procedure to separate the overlays
   into two groups}
end;

overlay procedure ProcC; 
begin
  Writeln('Overlay C'); 
end;

begin
  OvrDrive(2);
  ProcA;
  OvrDrive(0);
  ProcC;
  OvrDrive(2);
  ProcB;
end.

The first call to OvrDrive specifies overlays to be sought on the B: dnve The call to ProcA therefore causes the first overlay file (containing the two overlay procedures ProcA and ProcB to be opened here.

Next, the OvrDrive(0) statement specifies that following overlays are to be found on the logged drive. The call to ProcC opens the second over lay file here.

The following ProcB statement calls an overlay procedure in the first overlay file; and to ensure that it is sought on the B: drive, the OvrDrive(2) statement must be executed before the call.

266 TURBO Pascal Reference Manual

Files

Files

File Names

A file name in CP/M consists of one through eight letters or digits, op-tionally followed by a period and a file type of one through three letters or digits:

Drive: Name. Type

Text Files

The Seek and Flush procedures and the FilePos and FileSize functions are not applicable to CP/M text files.

Absolute Variables

Variables may be declared to reside at specific memory addresses, and are then called absolute. This is done by adding the reserved word ab-solute and an address expressed by an integer constant to the variable declaration.

Example:

var
IObyte: Byte absolute $0003;
CmdLine: string[127] absolute $80;

Absolute may also be used to declare a variable "on top" of another variable, i.e. that a variable should start at the same address as another variable. When absolute is followed by the variable (or parameter) identifier, the new variable will start at the address of that variable (or parameter).

Example:

var
Str: string[32];
StrLen: Byte absolute Str;

The above declaration specifies that the variable StrLen should start at the same address as the variable Str, and since the first byte of a string variable gives the length of the string, StrLen will contain the length of Str. Notice that only one identifier may be specified in an absolute de-claration, i.e. the construct:

CP/M-80 267

Absolute Variables

Identl, Ident2: Integer absolute $8000

is illegal. Further details on space allocation for variables are given on pages 278 and 288.

Addr Function

Syntax: Addr(name);

Returns the address in memory of the first byte of the type, variable, procedure, or function with the identifier name. If nime is an array, it may be subscribed, and if name is a record, specific fields may be selected. The value returned is of type Integer.

Predefined Arrays

TURBO Pascal offers two predefined arrays of type Byte, called Mem and Port, which are used to directly access CPU memory and data ports.

Mem Array

The predeclared array Mem is used to access memory. Each component of the array is a Byte, and indexes correspond to addresses in memory. The index type is Integer. When a value is assigned to a com-ponent of Mem, it is stored at the address given by the index expres-sion. When the Mem array is used in an expression, the byte at the ad-dress specified by the index is used.

Examples:

Mem[WsCursor]: = 2;
Mem[WsCursor+1]: = $1B;
Mem[WsCursor+2]: = Ord(' ');
IObyte: = Mem[3];
Mem[Addr+Offset]: = Mem[Addr];

268 TURBO Pascal Reference Manual

Predefined Arrays

Port Array

The Port array is used to access the data ports of the Z-80 CPU. Each element of the array represents a data port with indexes corresponding to port numbers. As data ports are selected by 8-bit addresses, the in-dex type is Byte. When a value is assigned to a component of Port, it is output to the port specified. When a component of Port is referenced in an expression, its value is input from the port specified.

The use of the port array is restricted to assignment and reference in expressions only, i.e. components of Port cannot function as variable parameters to procedures and functions. Furthermore, operations refer-ring to the entire Port array (reference without index) are not allowed.

Array Subscript Optimization

The X compiler directive allows the programmer to select whether array subsciiption should be optimized with regard to execution speed or to code size. The default mode is active, i.e. { $X+ }, which causes execu-tion speed optimization. When passive, i.e. { $X-}, the code size is minimized.

With Statements

The default ‘depth’ of nesting of With statements is 2, but the W direc-tive may be used to change this value to between 0 and 9. For each block, With statements require two bytes of storage for each nesting level allowed. Keeping the nesting to a minimum may thus greatly affect the size of the data area in programs with many subprograms.

CP/M-80 269

Pointer Related Items

Pointer Related Items

MemAvail

The standard function MemAvail is available to determine the available space on the heap at any given time. The result is an Integer, and if more than 32767 bytes is available, MemAvail returns a negative number. The correct number of free bytes is then calculated as 65536.0 + MemAvail. Notice the use of a real constant to generate a Real result, as the result is greater than GMaxInt. Memory management is discussed in further detail on page 288.

Pointers and Integers

The standard functions Ord and Ptr provide direct control of the address contained in a pointer. Ord returns the address contained in its pointer argument as an Integer, and Ptr converts its Integer argument into a pointer which is compatible with all pointer types.

These functions are extremely valuable in the hands of an experienced programmer as they allow a pointer to point to anywhere in memory If used carelessly, however, they are very dangerous, as a dynamic vari-able may be made to overwrite other variables, or even program code

270 TURBO Pascal Reference Manual

CP/M Function Calls

CP/M Function Calls

For the purpose of calling CP/M BDOS and BIOS routines, TURBO Pas-cal introduces two standard procedures: Bdos and Bios, and four stan-dard functions: Bdos, BdosHL, Bios, and BiosHL.

Details on BDOS and BIOS routines are found in the CP/M Operating System Manual published by Digital Research.

Bdos procedure and function

Syntax: Bdos(Func {, Param } );

The Bdos procedure is used to invoke CP/M BDOS routines. Func and Param are integer expressions. Func denotes the number of the called routine and is loaded into the C register. Param is optional and denotes a parameter which is loaded into the DE register pair. A call to address 5 then invokes the BDOS.

The Bdos function is called like the procedure and returns an Integer result which is the value returned by the BDOS in the A register.

BdosHL function

Syntax: BdosHL(Func {, Param } );

This function is exactly similar to the Bdos function above, except that the result is the value returned in the HL register pair.

CP/M-80 271

CP/M Function Calls

Bios procedure and function

Syntax: Bios(Func {, Param } );

The Bios procedure is used to invoke BIOS routines. Func and Param are integer expressions. Func denotes the number of the called routine, with 0 meaning the WBOOT routine, 1 the CONST routine, etc. I.e. the address of the called routine is Func * 3 plus the WBOOT address con-tained in addresses 1 and 2. Param is optional and denotes a parameter which is loaded into the BC register pair prior to the call.

The Bios function is called like the procedure and returns an integer result which is the value returned by the BIOS in the A register.

BiosHL function

Syntax: BiosHL(Func {, Param } );

This function is exactly similar to the Bios function above, except that the result is the value returned in the HL register pair.

User Written I/0 Drivers

For some applications it is practical for a programmer to define his own input and output drivers, i.e. routines which perform input and output of characters to and from external devices. The following drivers are part of the TURBO environment, and used by the standard I/0 drivers (although they are not available as standard procedures or functions):

function ConSt. boolean;
function Conln: Char;
procedure ConOut (Ch: Char);
procedure LstOut (Ch: Char);
procedure AuxOut (Ch: Char);
function Auxin: Char;
procedure UsrOut (Ch: Char);
function Usrln: Char;

272 TURBO Pascal Reference Manual

User Written I/0 Drivers

The ConSt routine is called by the function KeyPressed, the Conln and ConOut routines are used by the CON:, TRM:, and KBD: devices, the LstOut routine is used by the LST: device, the AuxOut and Auxin rou-tines are used by the AUX: device, and the UsrOut and Usrln routines are used by the USR: device.

By default, these drivers use the corresponding BIOS entry points of the CP/M operating system, i.e. ConSt uses CONST, Conln uses CONIN, ConOut uses CONOUT, LstOut uses LIST, AuxOut uses PUNCH, Auxin uses READER, UsrOut uses CONOUT, and Usrln uses CONIN. This, however, may be changed by the programmer by assigning the address of a self-defined driver procedure or a driver function to one of the fol-lowing standard variables:

Variable Contains the address of the
ConStPtr ConSt function
ConlnPtr Conln function
ConOutPtr ConOut procedure
LstOutPtr LstOut procedure
AuxOutPtr AuxOut procedure
AuxlnPtr Auxin function
UsrOutPtr UsrOut procedure
UsrlnPtr Usrln function

A user defined driver procedure or driver function must match the definitions given above, i.e. a ConSt driver must be a Boolean function, a Conln driver must be a Char function, etc.

CP/M-80 273

External Subprograms

External Subprograms

The reserved word external is used to declare external procedures and functions, typically procedures and functions written in machine code

An external subprogram has no block, i.e. no declaration part and no statement part. Only the subprogram heading is specified, immediately, followed by the reserved word external and an integer constant defining the memory address of the subprogram:

procedure DiskReset; external $EC00; 
function IOstatus: boolean; external $D123

Parameters may be passed to external subprograms, and the syntax is exactly the same as that of calls to ordinary procedures and functions

procedure Plot(X, Y: Integer); external $F003; 
procedure QuickSort(var List: PartNo); external $1C00;

Parameter passing to external subprograms is discussed further on page 283.

In-line Machine Code

TURBO Pascal features the inline statements as a very convenient way of inserting machine code instructions directly into the program text. An inline statement consists of the reserved word inline followed by one or more code elements separated by slashes and enclosed in parentheses.

A code element is built from one or more data elements, separated by plus (+ ) or minus ( - ) signs. A data element is either an integer con stant, a variable identifier, a procedure identifier, a function identifier, or a location counter reference. A location counter reference is written as an asterisk (*).

Example:

inline (10/$2345/count+1/sort-*+2);

274 TURBO Pascal Reference Manual

In-line Machine Code

Each code element generates one byte or one word (two bytes) of code. The value of the byte or the word is calculated by adding or subtracting the values of the data elements according to the signs that separate them. The value of a variable identifier is the address (or offset) of the variable. The value of a procedure or function identifier is the address (or offset) of the procedure oi function. The value of a location counter reference is the address (or offset) of the location counter, i.e. the ad-dress at which to generate the next byte of code.

A code element will generate one byte of code if it consists of integer constants only, and if its value is within the 8-bit range (0..255). If the value is outside the 8-bit range, or if the code element refers to variable, procedure, or function identifiers, or if the code element contains a loca-tion counter reference, one word of code is generated (least significant byte first).

The ‘ < ’ and ‘ > ’ characteis may be used to override the automatic size selection described above. If a code element starts with a ‘ < ’ character, only the least significant byte of the value is coded, even if it is a 16-bit value. If a code element starts with a ‘ > ’ character, a word is always coded, even though the most significant byte is zero.

Example:

inline (<$1234/>$44);

This inline statement generates three bytes of code: $34, $44, $00.

The following example of an inline statement generates machine code that will convert all characters in its string argument to upper case.

CP/M-80 275

In-line Machine Code

procedure UpperCase(var Strg: Str); {Str is type String[25
{$A+}
begin
  inline ($2A/Strg/       {       LD   HL,(Strg)   }
           $46/            {       LD   B,(HL)      }
           $04/            {       INC  B           }
           $05/            { L1:   DEC  B           }
           $CA/*+20/       {       JP   Z,L2        }
           $23/            {       INC  HL          }
           $7E/            {       LD   A,(HL)      }
           $FE/$61/        {       CP   'a'         }
           $DA/*-9/        {       JP   C,L1        }
           $FE/$7B/        {       CP   'z'+1       }
           $D2/*-14/       {       JP   NC,L1       }
           $D6/$20/        {       SUB  20H         }
           $77/            {       LD   (HL),A      }
           $C3/*-20);      {       JP   L1          }
                           { L2:   EQU  $           }
end;

Inline statements may be freely mixed with other statements throughout the statement part of a block, and inline statements may use all CPU registers. Note, however, that the contents of the stack pointer register (SP) must be the same on exit as on entry.

276 TURBO Pascal Reference Manual

Interrupt Handling

Interrupt Handling

The TURBO Pascal run time package and the code generated by the compiler are both foully interruptable. Interrupt service routines must preserve all registers used.

If required, interrupt service procedures may be written in Pascal. Such procedures should always be compiled with the A compiler directive ac-tive ({$A + }), they must not have parameters, and they must them-selves insure that all registers used are preserved. This is done by plac-ing an inline statement with the necessary PUSH instructions at the very beginning of the procedure, and another inline statement with the corresponding POP instructions at the very end of the procedure. The last instruction of the ending inline statement should be an El instruction ($FB) to enable further interrupts. If daisy chained interrupts are used, the inline statement may also specify a RETI instruction ($ED, $4D), which will override the RET instruction generated by the compiler.

The general rules for register usage are that integer operations use only the AF, BC, DE, and HL registers, other operations may use IX and IY, and real operations use the alternate registers.

An interrupt service procedure should not employ any I/0 operations us-ing the standard procedures and functions of TURBO Pascal, as these routines are not re-entrant. Also note that BDOS calls (and in some in-stances BIOS calls, depending on the specific CP/M implementation) should not be performed from interrupt handlers, as these routines are not re-entrant.

The programmer may disable and enable interrupts throughout a pro-gram using DI and El instructions generated by inline statements.

If mode 0 (IM 0) or mode 1 (IM 1) interrupts are employed, it is the responsibility of the programmer to initialize the restart locations in the base page (note that RST 0 cannot be used, as CP/M uses locations 0 through 7).

If mode 2 (IM 2) interrupts are employed, the programmer should gen-erate an initialized jump table (an array of integers) at an absolute address, and initialize the I register through a inline statement at the beginning of the program.

CP/M-80 277

Internal Data Formats

Internal Data Formats

In the following descriptions, the symbol @ denotes the address of the first byte occupied by a variable of the given type. The standard function Addr may be used to obtain this value for any variable.

Basic Data Types

The basic data types may be grouped into structures (arrays, records and disk files), but this structuring will not affect their internal formats

Scalars

The following scalars are all stored in a single byte: Integer subranges with both bounds in the range 0..255, Booleans, Chars, and declared scalars with less than 256 possible values. This byte contains the ordinal value of the variable.

The following scalars are all stored in two bytes: Integers, Integer subranges with one or both bounds not within the range 0..255, and de-clared scalars with more than 256 possible values. These bytes contain a 2’s complement 16-bit value with the least significant byte stored first

Reals

Reals occupy 6 bytes, giving a floating point value with a 40-bit mantissa and an 8-bit 2’s exponent. The exponent is stored in the first byte and the mantissa in the next five bytes which the least significant byte first

@ Exponent
@ + 1 LSB of mantissa
:
@ + 5 MSB of mantissa

The exponent uses binary format with an offset of $80. Hence, an ex-ponent of $84 indicates that the value of the mantissa is to be multiphed by 2 ^($84-$80) = 2 ^4 = 16. If the exponent is zero, the floating point value is considered to be zero.

278 TURBO Pascal Reference Manual

Internal Data Formats

The value of the mantissa is obtained by dividing the 40-bit unsigned in-teger by 2 ^40. The mantissa is always normalized, i.e. the most significant bit (bit 7 of the fifth byte) should be interpreted as a 1. The sign of the mantissa is stored in this bit, a 1 indicating that the number is negative, and a 0 indicating that the number is positive.

Strings

A string occupies the number of bytes corresponding to one plus the maximum length of the string. The first byte contains the current length of the string. The following bytes contain the actual characters, with the first character stored at the lowest address. In the table shown below, L denotes the current length of the string, and Max denotes the maximum length:

@ Current length (L)
@ + 1 First character
@ + 2 Second character
:
@ + L Last character
@ + L+ 1 Unused
:
@ + Max Unused

Sets

An element in a set occupies one bit, and as the maximum number of elements in a set is 256, a set variable will never occupy more than 32 bytes (256/8).

If a set contains less than 256 elements, some of the bits are bound to be zero at all times and need therefore not be stored. In terms of memory efficiency, the best way to store a set variable of a given type would then be to "cut off" all insignificant bits, and rotate the remaining bits so that the first element of the set would occupy the first bit of the first byte. Such rotate operations, however, are quite slow, and TURBO therefore employs a compromise: Only bytes which are statically zero (i.e. bytes of which no bits are used) are not stored. This method of compression is very fast and in most cases as memory efficient as the rotation method.

CP/M-80 279

Internal Data Formats

The number of bytes occupied by a set variable is calculated as (Max div 8) - (Min div 8) + 1, where Max and Min are the upper and lower bounds of the base type of that set. The memory address of a specific element E is:

MemAddress = @ + (E div 8) - (Min div 8)

and the bit address within the byte at MemAddress is:

BitAddress = E mod 8

where E denotes the ordinal value of the element.

File Interface Blocks

The table below shows the format of a FIB in TURBO Pascal-80:

@ + 0 Flags byte.
@ + 1 Character buffer.
@ + 2 Sector buffer pointer (LSB).
@ + 3 Sector buffer pointer (MSB).
@ + 4 Number of records (LSB).
@ + 5 Number of records (MSB).
@ + 6 Record length (LSB).
@ + 7 Record length (MSB).
@ + 8 Current record (LSB).
@ + 9 Current record (MSB).
@ + 10 Unused.
@ + 11 Unused.
@ + 12 First byte of CP/M FCB.
:
@ + 47 Last byte of CP/M FCB.
@ + 48 First byte of sector buffer.
:
@ + 175 Last byte of sector buffer.

The format of the flags byte at @+ 0 is:

Bit 0..3 File type.
Bit 4 Read semaphore.
Bit 5 Write semaphore.
Bit 6 Output flag.
Bit 7 Input flag.

280 TURBO Pascal Reference Manual

Internal Data Formats

File type 0 denotes a disk file, and 1 through 5 denote the TURBO Pas-cal logical I/0 devices (CON:, KBD:, LST:, AUX:, and USR:). For typed files, bit 4 is set if the contents of the sector buffer is undefined, and bit 5 is set if data has been written to the sector buffer. For textfiles, bit 5 is set if the character buffer contains a pre-read character. Bit 6 is set if output is allowed, and bit 7 is set if input is allowed.

The sector buffer pointer stores an offset (0..127) in the sector buffer at @ + 48. For typed and untyped files, the three words from @ + 4 to @ + 9 store the number of records in the file, the record length in bytes, and the current record number. The FIB of an untyped file has no sector buffer, and so the sector buffer pointer is not used.

When a text file is assigned to a logical device, only the flags byte and the character buffer are used.

Pointers

A pointer consists of two bytes containing a 16-bit memory address, and it is stored in memory using byte reversed format, i.e. the least significant byte is stored first. The value nil corresponds to a zero word.

Data Structures

Data structures are built from the basic data types using various struc-turing methods. Three different structuring methods exist: arrays, records, and disk files. The structuring of data does not in any way affect the internal formats of the basic data types.

Arrays

The components with the lowest index values are stored at the lowest memory address. A multi-dimensional array is stored with the rightmost dimension increasing first, e.g. given the array

Board: array[1..8,1..8] of Square

you have the following memory layout of its components:

CP/M-80 281

Internal Data Formats

lowest address: Board[1,1]
Board[1,2]
:
Board[1,8]
Board[2,1]
Board[2,2]
:
:
Highest address: Board[8,8]

Records

The first field of a record is stored at the lowest memory address. If the record contains no variant parts, the length is given by the sum of the lengths of the individual fields. If a record contains a variant, the total number of bytes occupied by the record is given by the length of the fixed part plus the length of largest of its variant parts. Each variant starts at the same memory address.

Disk Files

Disk files are different from other data structures in that data is not stored in internal memory but in a file on an external device. A disk file is controlled through a file interface block (FIB) as described on page 280 In general there are two different types of disk files: random access files and text files.

Random Access Files

A random access file consists of a sequence of records, all of the same length and same internal format. To optimize file storage capacity, the records of a file are totally contiguous. The first four bytes of the first 1 sector of a file contains the number of records in the file and the length of each record in bytes. The first record of the file is stored starting at the fourth byte.

sector 0, byte 0: Number of records (LSB)
sector 0, byte 1: Number of records (MSB)
sector 0, byte 2: Record length (LSB)
sector 0, byte 3: Record length (MSB)

282 TURBO Pascal Reference Manual

Internal Data Formats

Text Files

The basic components of a text file are characters, but a text file is sub-divided into lines. Each line consists of any number of characters ended by a CR/LF sequence (ASCII $0D/ $0A). The file is terminated by a Ctrl-Z (ASCII $1A).

Parameters

Parameters are transferred to procedures and functions via the Z-80 stack. Normally, this is of no interest to the programmer, as the machine code generated by TURBO Pascal will automatically PUSH parameters onto the stack before a call, and POP them at the beginning of the sub-program. However, if the programmer wishes to use external subpro-grams, these must POP the parameters from the stack themselves.

On entry to an external subroutine, the top of the stack always contains the return address (a word). The parameters, if any, are located below the return address, i.e. at higher addresses on the stack. Therefore, to access the parameters, the subroutine must first POP off the return ad-dress, then all the parameters, and finally it must restore the return ad-dress by PUSHing it back onto the stack.

Variable Parameters

With a variable (VAR) parameter, a word is transferred on the stack giv-ing the absolute memory address of the first byte occupied by the actual parameter.

Value Parameters

With value parameters, the data transferred on the stack depends upon the type of the parameter as described in the following sections.

Scalars

Integers, Booleans, Chars and declared scalars are transferred on the stack as a word. If the variable occupies only one byte when it is stored, the most significant byte of the parameter is zero. Normally, a word is POPped off the stack using an instruction like POP HL.

CP/M-80 283

Internal Data Formats

Reals

A real is transferred on the stack using six bytes. If these bytes are POPped using the instruction sequence:

   POP   HL
   POP   DE
   POP   BC

then L will contain the exponent, H the fifth (least significant) byte of the mantissa, E the fourth byte, D the third byte, C the second byte, and 8 the first (most significant) byte.

Strings

When a string is at the top of the staok, the byte pointed to by SP con tains the length of the string. The bytes at addresses SP+1 through SP+ n (where n is the length of the string) contain the string with the first character stored at the lowest address. The following machine code instructions may be used to POP the string at the top of the stack and store it in StrBuf.

   LD    DE, StrBuf
   LD    HL,0 
   LD    B,H 
   ADD   HL, SP
   LD    C, (HL)
   INC   BC
   LDIR
   LD    SP, HL

Sets

A set always occupies 32 bytes on the stack (set compression only ap-plies to the loading and storing of sets). The following machine code in structions may be used to POP the set at the top of the stack and store it in SetBuf:

284 TURBO Pascal Reference Manual

Internal Data Formats

   LD    DE,SetBuf
   LD    HL,0
   ADD   HL,SP
   LD    BC,32
   LDIR
   LD    SP,HL

This will store the least significant byte of the set at the lowest address in SetBuf.

Pointers

A pointer value is transferred on the stack as a word containing the memory address of a dynamic variable. The value NIL corresponds to a zero word.

Arrays and Records

Even when used as value parameters, Array and Record parameters are not actually PUSHed onto the stack. Instead, a word containing the ad-dress of the first byte of the parameter is tiansferred. It is then the responsibility of the subroutine to POP this word, and use it as the source address in a block copy operation.

Function Results

User written external functions must return their results exactly as specified in the following:

Values of scalar types, must be returned in the HL register pair. If the type of the result is expressed in one byte, then it must be returned in L and H must by zero.

Reals must be return@ in the BC, DE, and HL register pairs. B, C, D, E, and H must contain the mantissa (most significant byte in B), and L must contain the exponent.

Strings and sets must be returned on the top of the stack on the for-mats described on page 284.

Pointer values must be returned in the HL register pair.

CP/M-80 285

Internal Data Formats

The Heap and The Stacks

As indicated by the memory maps in previous sections, three stack-like structures are maintained during execution of a program: The heap the CPU stack, and the recursion stack.

The heap is used to store dynamic variables, and is controlled with the standard procedures New, Mark, and Release. At the beginning of a program, the heap pointer HeapPtr is set to the address of the bottom of free memory, i.e the first free byte after the object code.

The CPU stack is used to store intermediate results during evaluation of expressions and to transfer parameters to procedures and functions. An active for statement also uses the CPU stack, and occupies one word At the beginning of a program, the CPU stack pointer StackPtr is set to the address of the top of free memory.

The recursion stack is used only by recursive procedures and functions i.e. procedures and functions compiled with the A compiler directive pas-sive ({$A-}). On entry to a recursive subprogram it copies its workspace onto the recursion stack, and on exit the entire workspace is restored to its original state. The default initial value of RecurPtr at the beginning of a program, is 1K ($400) bytes below the CPU stack pointer.

Because of this technique, variables local to a subprogram must not be used as var parameters in recursive calls.

The pre-defined variables:

HeapPtr: The heap pointer,
RecurPtr: The recursion stack pointer, and
StackPtr: The CPU stack pointer

allow the programmer to control the position of the heap and the stacks

The type of these variables is Integer. Notice that HeapPtr and RecurPtr may be used in the same context as any other Integer variable, whereas StackPtr may only be used in assignments and expressions. When these variables are manipulated, always make sure that they point to addresses within free memory, and that:

HeapPtr < RecurPtr < StackPtr

286 TURBO Pascal Reference Manual

Internal Data Formats

Failure to adhere to these rules will cause unpredictable, perhaps fatal, results.

Needless to say, assignments to the heap and stack pointers must nev-er occur once the stacks or the heap are in use.

On each call to the procedure New and on entering a recursive pro-cedure or function, the system checks for collision between the heap and the recursion stack, i.e. checks if HeapPtr is less than RecurPtr. If not, a collision has occurred, which results in an execution error.

Note that no checks are made at any time to insure that the CPU stack does not overflow into the bottom of the recursion stack. For this to happen, a recursive subroutine must call itself some 300-400 times, which must be considered a rare situation. If, however, a program re-quires such nesting, the following statement executed at the beginning of the program block will move the recursion stack pointer downwards to create a larger CPU stack:

RecurPtr:= StackPtr -2 *MaxDepth -512;

where MaxDepth is the maximum required depth of calls to the recur-sive subprogram(s). The extra approx. 512 bytes are needed as a margin to make room for parameter transfers and intermediate results during the evaluation of expressions.

CP/M-80 287

Memory Management

Memory Management

Memory Maps

The following diagrams illustrate the contents of mem'ory at different ’ stages of working with the TURBO system. Solid lines indicate fixed boundaries (i.e. determined by amount of memory, size of your CP/M version of TURBO, etc.), whereas dotted lines indicate boundaries which are determined at run-time (e.g. by the size of the source text, and by possible user manipulation of various pointers, etc.). The sizes of the segments in the diagrams do not necessarily reflect the amounts of memory actually consumed.

Compilation in Memory

During compilation of a program in memory (Memory-mode on compiler Options menu, see page 259), the memory is mapped as follows:

Memory map

Figure 22-5: Memory map during compilation in memory

288 TURBO Pascal Reference Manual

Memory Management

If the error message file is not loaded when starting TURBO, the source text starts that much lower in memory. When the compiler is invoked, it generates object code working upwards from the end of the source text. The CPU stack works downwards from the logical top of memory, and the compiler’s symbol table works downwards from an address 1K ($400 bytes) below the logical top of memory.

Compilation To Disk

During compilation to a .COM or .CHN file (Com-mode or cHn-mode on compiler Options menu, see page 259), the memory looks much as dur-ing compilation in memory (see preceding section) except that generated object code does not reside in memory but is written to a disk file. Also, the code starts at a higher address (right after the Pascal library instead of after the source text). Compilation of much larger programs is thus possible in this mode.

Memory map

Figure 22-6: Memory map during compilation to a file

CP/M-80 289

Memory Management

Execution in Memory

When a program is executed in direct - or memory - mode (i.e. the Memory-mode on compiler Options menu is selected, see page 259), the memory is mapped as follows:

Memory map

Figure 22-7: Memory map during execution in direct mode

When a program is compiled, the end of the object code is known. The heap pointer HeapPtr is set to this value by default, and the heap grows from here and upwards in memory towards the recursion stack. The maximum memory size is BDOS minus one (indicated on the compiler Options menu). Program variables are stored from this address and downwards. The end of the variables is the ‘top of free memory’ which is the initial value of the CPU stack pointer StackPtr. The CPU stack grows downwards from here towards the position of the recursion stack pointer RecurPtr, $400 bytes lower than StackPtr. The recursion stack grows from here downward towards the heap.

290 TURBO Pascal Reference Manual

Memory Management

Execution of A Program File

When a program file is executed (either by the Run command with the Memory-mode on the compiler Options menu selected, by an eXecute command, or directly from CP/M), the memory is mapped as follows:

Memory map

Figure 22-8: Memory map during execution of a program file

This map resembles the previous, except for the absence of the TURBO interface, editor, and compiler (and possible error messages) and of the source text. The default program start address (shown on the compiler Options menu) is the first free byte after the Pascal runtime library. This value may be manipulated with the Start address command of the com-piler Options menu, e.g. to create space for absolute variables and/or external procedures between the library and the code. The maximum memory size is BDOS minus one, and the default value is determined by the BDOS location on the computer in use.

CP/M-80 291

Memory Management

If programs are to be translated for other systems, care should be taken to avoid collision with the BDOS. The maximum memory may be mani-pulated with the End address command of the compiler Options menu. Notice that the default end address setting is approx. 700 to 1000 bytes lower than maximum memory. This is to allow space for the loader which resides just below BDOS when .COM files are Run or eXecuted from the TURBO system. This loader restores the TURBO editor, com-piler, and possible error messages when the program finishes and thus returns control to the TURBO system.

292 TURBO Pascal Reference Manual