JRT Pascal User's Guide version 3.0 NOT FOR SALE -55- 6. Builtin procedures Several builtin procedures are provided in Pascal. Most of these relate to input/output processing and are dicussed in the input/output section of this manual. The remaining procedures are covered in this section. A list of them and their purpose follows. JRT Pascal extensions are marked with an asterisk. procedure purpose --------- --------- * CALL direct access to CP/M and BIOS * DELETE delete portion of dynamic string DISPOSE de-allocate dynamic variables * FILLCHAR initialize a string * INSERT insert string into dynamic string * MAP access main storage NEW alloccate dynamic variables * PORTOUT hardware port output * SYSTEM EXEC services Copy compliments of Merle Schnick SECTION 6: Builtin Procedures JRT Pascal User's Guide version 3.0 NOT FOR SALE -56- 6.1 CALL Format CALL( address, parameter_regs, returned_regs ); The CALL builtin procedure allows the user to make direct calls to the CP/M operating system, to your own Basic Input/Output System (BIOS), and to any machine language code present in main storage. The 8080 data registers can be directly setup for passing parameters to the module called. The 8080 data registers which are returned from the module may contain return values which can be used directly from Pascal programs. Note that this assembly language interface complements the external procedure assembler. User subroutines which must be written in assembler will usually be written as external procedures and assembled. That gives the advantage of fully automatic loading and relocation. CALL is intended primarily for direct access to the operating system services. The address field is an integer expression. This field is regarded as an unsigned 16-bit integer. When CALL is executed, control is transferred to the machine code at the address. The module there MUST return control to Pascal with a RET instruction. The 8080 stack MUST NOT be modified on return to Pascal. The 8080, 8085 and Z80 microcomputers have 7 one-byte data registers and a one-byte flag register. The Z80 has additional registers but these are not used in a CP/M environment. Six of the data registers can be grouped as two-byte registers for some uses. 8080 Register Map --------------------- ! A ! FLAGS ! --------------------- ! B ! C ! --------------------- ! D ! E ! --------------------- ! H ! L ! --------------------- Copy compliments of Merle Schnick SECTION 6: Builtin Procedures JRT Pascal User's Guide version 3.0 NOT FOR SALE -57- The parameter_regs and returned_regs fields have a particular format which must be declared in your program. The parameter_regs field is directly loaded into the microprocessor data registers before control is transferred to the called module. When control is returned to Pascal, the current data registers are stored into the field identified by returned regs. Both of these fields should be declared like this: TYPE DATA_REGISTERS = RECORD CASE INTEGER OF 1 : ( FLAG,A,C,B,E,D,L,H : CHAR ); 2 : ( PSW,BC,DE,HL : INTEGER ); END; This is a variant record which defines the data registers for access in one or two bytes at a time. For example, sometimes it may be necessary to regard the register pair DE as an integer, other times it may be necessary to treat register E alone as a single byte. Both definitions total 8 bytes. NOTE that in definition 1 above that the register names are in an unusual sequence. This is necessary because the 8080/Z80 microprocessors store 16 bit data in "byte-reverse" format. Examples: VAR PARM_REGS, RETURNED_REGS : DATA_REGISTERS; CALL( 5, PARM_REGS, RETURNED_REGS ); 6.1.1 Calling the CP/M operating system An operating system is a program which provides services to application programs running under it. Some of these services are "create file", "write string to printer", "reinitialize system", and so on. Using the CALL builtin procedure the user can directly access these services from his Pascal programs. Copy compliments of Merle Schnick SECTION 6: Builtin Procedures JRT Pascal User's Guide version 3.0 NOT FOR SALE -58- The CP/M and MP/M User's Guides describe in detail the services provided and parameters required for each. Each service is identified by a one- byte function code. This code is stored in register C before control is transferred to CP/M. Many services also require an integer parameter such as an address in register pair DE. The entry point address for all CP/M compatible systems is location 5. At address 5 is stored a jump instruction to the actual CP/M module. The address of the BIOS (warm-start entry point) is stored at address 0001 in main storage and may be accessed with the MAP builtin procedure. The MAP and CALL procedures allow direct access to all of the services provided by the BIOS. Copy compliments of Merle Schnick SECTION 6: Builtin Procedures JRT Pascal User's Guide version 3.0 NOT FOR SALE -59- The service codes for CP/M 2.2 and MP/M are: code service ---- -------- 0 system reset 1 console input 2 console output 3 reader input 4 punch output 5 printer output 6 direct console input/output 7 get I/O byte 8 set I/O byte 9 print string 10 read console buffer 11 get console status 12 return version number 13 reset disk system 14 select disk 15 open existing file 16 close file 17 search for first file control block 18 search for next file control block 19 delete file 20 read sequential 21 write sequential 22 create file 23 rename file 24 return login vector 25 return current disk 26 set DMA address 27 get addr (alloc) 28 write protect disk 29 get read/only vector 30 set file attributes 31 get addr (disk parms) 32 set/get user code 33 read random record 34 write random record 35 compute file size 36 set random record 37 reset drive 40 write random with zero fill Copy compliments of Merle Schnick SECTION 6: Builtin Procedures JRT Pascal User's Guide version 3.0 NOT FOR SALE -60- The following services are available in MP/M only: code service ---- -------- 128 absolute memory request 129 relocatable memory request 130 memory free 131 poll 132 flag wait 133 flag set 134 create queue 135 open queue 136 delete queue 137 read queue 138 conditional read queue 139 write queue 140 conditional write queue 141 delay 142 dispatch 143 terminate process 144 create process 145 set priority 146 attach console 147 detach console 148 set console 149 assign console 150 send CLI command 151 call resident system process 152 parse filename 153 get console number 154 system data address 155 get date and time Copy compliments of Merle Schnick SECTION 6: Builtin Procedures JRT Pascal User's Guide version 3.0 NOT FOR SALE -61- Examples: 1. (* GET THE VERSION NUMBER FROM CP/M *) PROCEDURE GET_VERSION; VAR PARM_REGS, RETURN_REGS : DATA_REGISTERS; BEGIN (* SET FUNCTION CODE := 12 *) PARM_REGS.C := CHR(12); CALL( 5, PARM_REGS, RETURN_REGS ); (* THE CP/M VERSION NUMBER IS RETURNED IN REGISTER L. IF REGISTER H IS 01 THEN THE OPERATING SYSTEM IS MP/M *) CASE ORD( RETURNED_REGS.H ) OF 0 : WRITE('CP/M '); 1 : WRITE('MP/M '); ELSE : WRITE('????'); END; WRITE(' VERSION '); CASE HEX$( RETURNED_REGS.L ) OF '00' : WRITELN('1.X'); '20' : WRITELN('2.0'); '22' : WRITELN('2.2'); ELSE : WRITELN( HEX$( RETURNED_REGS.L )); END; END; (* GET_VERSION *) 2. PROCEDURE WRITE_PROTECT_CURRENT_DISK; VAR PARM_REGS, RETURNED_REGS : DATA_REGISTERS; BEGIN PARM_REGS.C := CHR(28); CALL( 5, PARM_REGS, RETURNED_REGS ); END; Copy compliments of Merle Schnick SECTION 6: Builtin Procedures JRT Pascal User's Guide version 3.0 NOT FOR SALE -62- 3. PROCEDURE GET_USER_CODE; VAR PARM_REGS, RETURNED_REGS : DATA_REGISTERS; BEGIN PARM_REGS.C := CHR(32); CALL( 5, PARM_REGS, RETURNED_REGS ); WRITELN('USER CODE = ',ORD( RETURNED_REGS.A )); END; 4. PROCEDURE SEARCH_FOR_FIRST ( NAME, TYPE : STRING[8] ); TYPE FILE_CONTROL_BLOCK = RECORD DISK : CHAR; FILENAME : ARRAY [1..8] OF CHAR; FILETYPE : ARRAY [1..3] OF CHAR; EXTENT : CHAR; S1, S2 : CHAR; RECORD_COUNT : CHAR; BLOCKS : ARRAY [1..16] OF CHAR; CURRENT_RECORD : CHAR; R0, R1, R2 : CHAR; END; VAR FCB : FILE_CONTROL_BLOCK; PARM_REGS, RETURNED_REGS : DATA_REGISTERS; BEGIN (* SET UP FCB *) FCB.DISK := CHR(0); FCB.FILENAME := NAME; FCB.FILETYPE := TYPE; (* SET UP PARM_REGS *) PARM_REGS.C := CHR(17); PARM_REGS.DE := ADDR(FCB); CALL( 5, PARM_REGS, RETURNED_REGS ); (* TEST RETURN CODE *) IF RETURNED_REGS.A = CHR(255) THEN WRITELN('FILE NOT FOUND'); END; Copy compliments of Merle Schnick SECTION 6: Builtin Procedures JRT Pascal User's Guide version 3.0 NOT FOR SALE -63- 6.2 DELETE Format DELETE( string_variable, position, length ); The DELETE builtin procedure is used to delete a number of characters from a dynamic string variable. The first parameter refers to the string variable, NOT a string expression. The second parameter is an integer expression which indicates the first character to be deleted (characters in dynamic strings are numbered from 1). The third parameter is an integer expression which indicates the number of characters to be deleted. The hidden length field of the dynamic string variable is updated. If the position and length parameters refer to an area beyond the current length of the string, a run-time error occurs. Examples: DELETE( TARGET_STR, 25, 3 ); DELETE( STR1, POS( 'END', STR1), 3 ); DELETE( STR3, 9, X + 3 ); Copy compliments of Merle Schnick SECTION 6: Builtin Procedures JRT Pascal User's Guide version 3.0 NOT FOR SALE -64- 6.3 DISPOSE Format DISPOSE( pointer_variable ); The DISPOSE builtin procedure is used to de-allocate dynamic variables. The pointer-variable addresses a dynamic variable in dynamic storage. After execution of the procedure the space released is available for other uses. JRT Pascal supports true dynamic storage with auto-compression. When blocks are freed up, storage fragmentation tends to occur, i.e., small unused blocks tend to accumulate. Because many blocks tend to be small, they cannot be immediately reused for another purpose. When storage becomes short, an auto-compression is initiated by the Pascal system. In this process, all freed blocks are gathered into the center area of storage and all needed blocks are moved to the top of storage. In this way, storage fragmentation is totally eliminated. The DISPOSE procedure can be used to de-allocate ghost variables created by the MAP builtin procedure. Although ghost variables use no real storage, they do require a small amount of space in the pointer tables. Example: PROCEDURE DISPOSE_DEMO; TYPE DYN_VAR = ARRAY [1..200] OF CHAR; VAR POINTER : ^DYN_VAR; BEGIN NEW( POINTER ); (* ALLOCATE A DYNAMIC VARIABLE *) (* DO SOME PROCESSING WITH THE DYNAMIC VARIABLE *) DISPOSE( POINTER ); (* FREE UP THE 200 BYTES *) END; Copy compliments of Merle Schnick SECTION 6: Builtin Procedures JRT Pascal User's Guide version 3.0 NOT FOR SALE -65- 6.4 FILLCHAR Format FILLCHAR( structured_variable, length, character ); The FILLCHAR builtin procedure is a very fast and simple way to initialize a structured variable (array or record) to a character. The length parameter is an integer expression which indicates the number of bytes to be initialized. The entire variable from its first byte up to the length specified is set to the character expression value. CAUTION - This is a hazardous procedure since the run-time system cannot verify that the initialization by character has not run past the end of the variable and perhaps overlayed other variables or program code. Examples: FILLCHAR( VECTOR, 160, CHR(0) ); FILLCHAR( PRODUCT_ARRAY, 2500, '*' ); Copy compliments of Merle Schnick SECTION 6: Builtin Procedures JRT Pascal User's Guide version 3.0 NOT FOR SALE -66- 6.5 INSERT Format INSERT( source_string, target_string_variable, position ); The INSERT builtin procedure inserts the source string expression into the target string variable at the indicated position. The source string may be a literal string or other string expression. The target string must be an actual variable. The source string is inserted into the target variable beginning at the character indicated by the integer expression position. If the combination of parameters would cause the target string to overflow its maximum length or if position is less than 1, a run-time error occurs. Examples: INSERT( 'ABCD', STR1, 15 ); INSERT( FILENAME, MASK, 1 ); STR1 := 'MERE FACTICITY.'; INSERT( 'TRUTH IS NOT ', STR1, 1 ); Copy compliments of Merle Schnick SECTION 6: Builtin Procedures JRT Pascal User's Guide version 3.0 NOT FOR SALE -67- 6.6 MAP Format MAP( pointer_variable, address ); The MAP procedure allows the user to access any part of the computer's storage. It uses the facilities of the dynamic storage system and pointer variable to, in effect, overlay a map on any area of storage. This is sometimes called a "dsect" or "ghost variable". Unlike its close relative, the NEW procedure, MAP does not actually allocate a dynamic storage block. Instead of obtaining a storage block and setting the pointer variable to point at it, it lets the user specify the address. The address can be anywhere from 0 to 0FFFFH. Like the NEW procedure, MAP does require five bytes of pointer table space. When the ghost variable is no longer needed, it can be removed from the table with the DISPOSE procedure. Examples: 1. (* ACCESS A 24 X 80 VIDEO TERMINAL *) (* IT IS A MEMORY-MAPPED MODEL WITH ITS *) (* VIDEO SCREEN BEGINNING AT 0F000H *) TYPE SCREEN = ARRAY [1..24, 1..80] OF CHAR; VAR CRT : ^SCREEN; BEGIN MAP(CRT, 0F000H ); (* CLEAR THE SCREEN *) CRT^ := ' '; (* WRITE MESSAGE ON TOP LINE OF CRT *) CRT^[1] := 'MEMORY MAPPED CRT EXAMPLE'; ... END; Copy compliments of Merle Schnick SECTION 6: Builtin Procedures JRT Pascal User's Guide version 3.0 NOT FOR SALE -68- 2. (* OBTAIN THE ADDRESS OF THE USER BIOS. *) (* JMP INSTRUCTION AT ADDR 0 ADDRESSES *) (* THE WARM-START ENTRY POINT IN BIOS *) FUNCTION BIOS : INTEGER; VAR PTR : ^INTEGER; BEGIN MAP( PTR, 1 ); BIOS := (PTR^ -3); (* START OF BIOS *) END; 3. (* SET THE IOBYTE AT ADDR 3 TO NEW VALUE *) PROCEDURE SET_IOBYTE ( X : CHAR ); VAR PTR : ^CHAR; BEGIN MAP( PTR, 3 ); PTR^ := X; DISPOSE( PTR ); END; Copy compliments of Merle Schnick SECTION 6: Builtin Procedures JRT Pascal User's Guide version 3.0 NOT FOR SALE -69- 6.7 NEW Format 1 NEW( pointer_variable ); Format 2 NEW( pointer_variable, tag1,...,tagn ); The NEW procedure allocates new dynamic variables. A block of dynamic storage of the required size is obtained. The block's virtual address (not its actual address) is stored in the pointer table. Virtual addressing and dynamic storage are fully explained in the section on storage management in this manual. After NEW has been executed, the dynamic variable may be accessed. Dynamic variables remain allocated until specifically de-allocated by the DISPOSE procedure. If a procedure uses NEW to allocate a dynamic variables, that variable remains allocated after the procedure ends. Format 2 contains 1 to n tag fields. These are the fields specified in the CASE clause of variant records. Copy compliments of Merle Schnick SECTION 6: Builtin Procedures JRT Pascal User's Guide version 3.0 NOT FOR SALE -70- Example: (* PROGRAM FRAGMENT TO ALLOCATE A *) (* LINKED LIST OF VARIABLE LENGTH. *) (* THE ROOT OF THE LIST IS A GLOBAL *) (* VARIABLE. NODES AFTER THE FIRST *) (* ARE INSERTED BETWEEN THE ROOT AND *) (* THE FIRST NODE. *) TYPE NODE = RECORD NEXT : INTEGER; DATA : STRING[300]; END; VAR ROOT : ^NODE; PROCEDURE LINKED_LIST ( COUNT : INTEGER ); VAR I : INTEGER; TEMP : ^NODE; BEGIN (* ALLOCATE FIRST NODE *) NEW( ROOT ); (*SET END_OF_LIST INDICATOR *) ROOT^.NEXT := NIL; (* ALLOCATE LINKED LIST *) FOR I := 1 TO COUNT DO BEGIN NEW( TEMP ); TEMP^.NEXT := ROOT; ROOT := TEMP; END; END; (* LINKED_LIST *) Copy compliments of Merle Schnick SECTION 6: Builtin Procedures JRT Pascal User's Guide version 3.0 NOT FOR SALE -71- 6.8 PORTOUT Format PORTOUT( port_number, byte ); The PORTOUT procedure writes a byte directly to one of the hardware output ports. The port_number is an integer expression. The byte is a string or char expression. Examplels: PORTOUT( MODEM, START_CHAR ); PORTOUT( VOICE_SYNTHESIZER, 'A' ); PORTOUT( FIRE_ALARM, RESET ); PORTOUT( TELETYPE, CHR(7) ); PORTOUT( 15H, CHR( 3 + X )); Copy compliments of Merle Schnick SECTION 6: Builtin Procedures JRT Pascal User's Guide version 3.0 NOT FOR SALE -72- 6.9 SYSTEM Format SYSTEM( option ); The SYSTEM procedure allows the user to control the trace facililties, the routing of console output, dynamic storage compression and warning messagges. The options for SYSTEM are listed below. The default states of the JRT Pascal system are indicated with an asterisk: option purpose ------- -------- * CONS route output to console NOCONS no output to console LIST route output to printer * NOLIST no output to printer * WARNING display warning messages NOWARNING suppress warning messages LTRACE activate line trace * NOLTRACE disable line trace LRANGE,l,u set line range for line trace PTRACE activate procedure trace * NOPTRACE disable procedure trace INITIALIZE re-initialize disk system after disk switch COMPRESS compress dynamic storage The LRANGE option requires two additional parameters. The lower and upper line numbers are integer expressions. Examples: SYSTEM( LIST ); SYSTEM( NOWARNING ); SYSTEM( LRANGE, 250, 300 ); SYSTEM( COMPRESS ); Copy compliments of Merle Schnick SECTION 6: Builtin Procedures M( LIST ); SYSTEM( NOWARNING ); SYSTEM( LRANGE, 250, 300 ); SYSTEM( COMPRESS ); Copy compliments of Merle Schnick