The PL/I-80 Runtime Subroutine Library (PLILIB.IRL) is discussed
in this section, along with the optional subroutines for direct CP/M Input
Output. The information given here is useful when PL/I-80 is used as
a "systems language," rather than an application language, since direct
access to implementation dependent CP/M functions is allowed. Note that
the use of these features makes your program very machine and operating
system dependent.
5.1. Stack and Dynamic Storage Subroutines
A number of implementation-dependent functions are included in the
PL/I-80 Runtime Library which provide access to stack and dynamic storage
structures. The functions are discussed below, with sample programs which
illustrate their use. The stack is placed above the code and data area,
and below the dynamic storage area. The default value of the stack size
is 512 bytes, but can be changed using the STACK(n) option in the OPTIONS
portion of the main program procedure heading. In general, the PL/I-80
dynamic storage mechanism maintains a list of all unallocated storage.
Upon each request for storage, a search is made to find the first memory
segment which satisfies the request size. If no storage is found, the ERROR(7)
condition is signaled (Free Space Exhausted) otherwise, the requested segment
is taken from the free area, and the remaining portion goes back to the
free space list. In version 1.0 of PL/I-80, storage is dynamically
allocated only upon entry to RECURSIVE procedures, upon explicit or implicit
OPENS for files which access the disk, or upon executing an ALLOCATE statement.
In any case, an even number of bytes, or whole words, is always allocated,
no matter what the request size.
5.1.1. The TOTWDS and MAXWDS Functions
It is often useful to find the amount of storage available at any given
point in the execution of a particular program. The TOTWDS (Total Words)
and MAXWDS (Max Words) functions can be used to obtain this information.
The functions must be declared in the calling program as
dcl totwds returns(fixed(15));
dcl maxwds returns(fixed(15));
When invoked, the TOTWDS subroutine scans the free storage list
and returns the total number of words (double bytes) available in the free
list. The MAXWDS subroutine performs a similar function, but returns the
size of the largest segment in the free list, again in words. A subsequent
ALLOCATE statement which specifies a segment size not exceeding MAXWDS
will not cause the ERROR(7) signal to be raised, since at least that much
storage is available. Note that since both TOTWDS and MAXWDS count in word
units, the values can be held by FIXED BINARY(15) counters. If, during
the scan of free memory, invalid link words are encountered (usually due
to a out-of-bounds subscript or pointer store operation), both
TOTWDS and MAXWDS return the value -1. Otherwise, the returned value
will be a non-negative integer value.
The PL/I-80 Runtime Library contains a subroutine, called ALLWDS,
which is useful in controlling the dynamic allocation size. The subroutine
must be declared in the calling program as
dcl allwds entry(fixed(15)) returns(ptr);
The ALLWDS subroutine allocates a segment of memory of the size
given by the input parameter, in words (double bytes). If no segment is
available, the ERROR(7) condition is raised. Further, the input value must
be a non-negative integer value. The ALLWDS function returns a pointer
to the allocated segment.
An example of the use of TOTWDS, MAXWDS, and ALLWDS functions is given
in the ALLTST program on the next page. A sample program interaction is
given following the program listing.
The function STKSIZ (Stack Size) returns the current stack size in bytes
whenever it is called. This function is particularly useful for checking
possible stack overflow conditions, or in determining the maximum stack
depth during program testing. The STKSIZ function is declared in the calling
program as
dcl stksiz returns(fixed(15));
A Sample use of the STKSIZ function appears in the listing of the
recursive Ackermann test. In this case, it is
used to check the maximum stack depth during the recursive function processing.
An interaction with this program is given following
the program listing.
PL/I-80 V1.0, COMPILATION OF: ALLTST L: List Source Program NO ERROR(S) IN PASS 1 NO ERROR(S) IN PASS 2 PL/I-80 V1.0, COMPILATION OF: ALLTST 1 a 0000 alltst: 2 a 0006 proc options(main); 3 a 0006 /* assembly language interface to 4 a 0006 dynamic storage allocation module */ 5 c 0006 dcl 6 c 0006 totwds returns(fixed(15)), 7 c 0006 maxwds returns(fixed(15)), 8 c 0006 allwds entry(fixed(15)) returns(ptr); 9 c 0006 10 c 0006 dcl 11 c 0006 allreq fixed(15), 12 c 0006 memptr ptr, 13 c 0006 meminx fixed(15), 14 c 0006 memory (0:0) bit(16) based(memptr); 15 c 0006 16 c 0006 do while('1'b); 17 c 0006 put edit (totwds(),' Total Words Available', 18 c 004F maxwds(),' Maximum Segment Size', 19 c 004F 'Allocation Size? ') 20 c 004F (2(skip,f(6),a),skip,a); 21 c 004F get list(allreq); 22 c 0067 memptr = allwds(allreq); 23 c 0070 put edit('Allocated',allreq, 24 c 00B2 ' Words at ',unspec(memptr)) 25 c 00B2 (skip,a,f(6),a,b4); 26 c 00B2 27 c 00B2 /* clear memory as example */ 28 c 00B2 do meminx = 0 to allreq-1; 29 c 00CC memory(meminx) = '0000'b4; 30 c 00E7 end; 31 c 00E7 end; 32 a 00E7 end alltst; CODE SIZE = 00E7 DATA AREA = 0078 END COMPILATION A>b:alltst 25596 Total Words Available 25596 Maximum Segment Size Allocation Size? 0 Allocated 0 Words at 250A 25594 Total Words Available 25594 Maximum Segment Size Allocation Size? 100 Allocated 100 Words at 250E 25492 Total Words Available 25492 Maximum Segment Size Allocation Size? 25000 Allocated 25000 Words at 25DA 490 Total Words Available 490 Maximum Segment Size Allocation Size? 490 Allocated 490 Words at E92E 0 Total Words Available 0 Maximum Segment Size Allocation Size? 1 ERROR (7), Free Space Exhausted Traceback: 016D End of Execution
PL/I-80 V1.0, COMPILATION OF: ACKTST L: List Source Program NO ERROR(S) IN PASS 1 NO ERROR(S) IN PASS 2 PL/I-80 V1.0, COMPILATION OF: ACKTST 1 a 0000 ack: 2 a 0006 procedure options(main,stack(2000)); 3 c 0006 dcl 4 c 0006 (m,n) fixed, 5 c 0006 (maxm,maxn) fixed, 6 c 0006 ncalls decimal(6), 7 c 0006 (curstack, stacksize) fixed, 8 c 0006 stksiz entry returns(fixed); 9 c 0006 10 c 0006 put skip list('Type max m,n: '); 11 c 0022 get list(maxm,maxn); 12 c 0046 do m = 0 to maxm; 13 c 005F do n = 0 to maxn; 14 c 0078 ncalls = 0; 15 c 0088 curstack = 0; 16 c 008E stacksize = 0; 17 c 0091 put edit 18 c 012F ('Ack(',m,',',n,')=',ackermann(m,n), 19 c 012F ncalls,' Calls,',stacksize,' Stack Bytes') 20 c 012F (skip,a,2(f(2),a),f(6),f(7),a,f(4),a); 21 c 012F end; 22 c 012F end; 23 c 012F stop; 24 c 0132 25 c 0132 ackermann: 26 c 0132 procedure(m,n) returns(fixed) recursive; 27 e 0132 dcl 28 e 015C (m,n) fixed; 29 e 015C ncalls = ncalls + 1; 30 e 0177 curstack = stksiz(); 31 e 017D if curstack > stacksize then 32 e 018A stacksize = curstack; 33 e 0190 if m = 0 then 34 e 0199 return(n+1); 35 e 01A1 if n = 0 then 36 e 01AA return(ackermann(m-1,1)); 37 e 01C1 return(ackermann(m-1,ackermann(m,n-1))); 38 c 01E8 end ackermann; 39 a 01E8 end ack; CODE SIZE = 01E8 DATA AREA = 0084 END COMPILATION
A>b:acktst Type max m,n: 6,6 Ack( 0, 0)= 1 1 Calls, 4 Stack Bytes Ack( 0, 1)= 2 1 Calls, 4 Stack Bytes Ack( 0, 2)= 3 1 Calls, 4 Stack Bytes Ack( 0, 3)= 4 1 Calls, 4 Stack Bytes Ack( 0, 4)= 5 1 Calls, 4 Stack Bytes Ack( 0, 5)= 6 1 Calls, 4 Stack Bytes Ack( 0, 6)= 7 1 Calls, 4 Stack Bytes Ack( 1, 0)= 2 2 Calls, 6 Stack Bytes Ack( 1, 1)= 3 4 Calls, 8 Stack Bytes Ack( 1, 2)= 4 6 Calls, 10 Stack Bytes Ack( 1, 3)= 5 8 Calls, 12 Stack Bytes Ack( 1, 4)= 6 10 Calls, 14 Stack Bytes Ack( 1, 5)= 7 12 Calls, 16 Stack Bytes Ack( 1, 6)= 8 14 Calls, 18 Stack Bytes Ack( 2, 0)= 3 5 Calls, 10 Stack Bytes Ack( 2, 1)= 5 14 Calls, 14 Stack Bytes Ack( 2, 2)= 7 27 Calls, 18 Stack Bytes Ack( 2, 3)= 9 44 Calls, 22 Stack Bytes Ack( 2, 4)= 11 65 Calls, 26 Stack Bytes Ack( 2, 5)= 13 90 Calls, 30 Stack Bytes Ack( 2, 6)= 15 119 Calls, 34 Stack Bytes Ack( 3, 0)= 5 15 Calls, 16 Stack Bytes Ack( 3, 1)= 13 106 Calls, 32 Stack Bytes Ack( 3, 2)= 29 541 Calls, 64 Stack Bytes Ack( 3, 3)= 61 2432 Calls, 128 Stack Bytes Ack( 3, 4)= 125 10307 Calls, 256 Stack Bytes Ack( 3, 5)=
5.2. PL/I-80 Runtime Subroutine Entry Points
The standard PL/I-80 Runtime Library entry points are listed below. The entry point name is shown to the left, followed by the input value registers and the result registers. A short explanation is given on the right. Note that this list does not include the environmental or I/O operators since these entry points may vary from version to version. Further, the definitions shown below are for general information purposes only, and are subject to change without notice. The register names are given in capital letters, M(r) denotes memory addressed by the register pair r, and ST represents a stacked value.
name parameters result comment or definition ---- ---------- ------ --------------------- im22n DE HL HL word*word integer multiply id22n DE HL HL word/word integer divide is22n DE HL HL word-word integer subtract in20n HL HL -word fl40m HL ST fp load from M(HL) to stack fx44s ST HL M(HL) fp xfer from stack to M(HL) fx44m DE HL M(HL) fp xfer from M(HL) to M(DE) fa44s ST ST ST fp add stack+stack to stack fa44m DE HL ST fp add M(DE)+M(HL) to stack fa44l ST HL ST fp add stack+M(HL) to stack fa44r HL ST ST fp add M(HL)+stack to stack fs44s ST ST ST fp sub stack-stack to stack fs44m DE HL ST fp sub M(DE)-M(HL) to stack fs44l ST HL ST fp sub stack-M(HL) to stack fs44r HL ST ST fp sub M(HL)-stack to stack fm44s ST ST ST fp mul stack*stack to stack fm44m DE HL ST fp mul M(DE)*M(HL) to stack fm44l ST HL ST fp mul stack*M(HL) to stack fm44r HL ST ST fp mul M(HL)*stack to stack fd44s ST ST ST fp div stack/stack to stack fd44m DE HL ST fp div M(DE)/M(HL) to stack fd44l ST HL ST fp div stack/M(HL) to stack fd44r HL ST ST fp div M(HL)/stack to stack fc44s ST ST ST fp comp stack:stack to stack fc44m DE HL ST fp comp M(DE):M(HL) to stack fc44l ST HL ST fp comp stack:M(HL) to stack fc44r HL ST ST fp comp M(HL):stack to stack fn40s ST ST fp negate stack fn40m HL ST fp load from M(HL) and negate fe40s ST A float p extract sign from stack fe40m HL A float p extract sign from memory 1 => positive sign (non zero set) 0 => zero result (zero flag set) -1 => negative sign (minus set) fmodf ST ST ST floating point mod(x,y) fabsf ST ST floating point abs(x) fmaxf ST ST ST floating point max(x,y) fminf ST ST ST floating point min(x,y) froun ST A ST floating point round(x,k) ftrnc ST ST floating point trunc(x) fflor ST ST floating point floor(x) fceil ST ST floating point ceil(x) fexop ST A ST fp ** k (k pos constant) ffxop ST ST ST x ** y (exp(y*log(x)) bc12n D HL HL 8/16 bit concatenate, where B=length of d, C=mask bc22n DE HL HL 16/16 bit concatenate, where B=length of d, C=mask bsl16 B HL HL bit shift left 16, size in b bsl08 A B A bit shift left 8, size in b bst08 A B C HL M(HL) bit substring store bit(8) in A to bit(8) in memory at HL, B = index, C=length bst16 B C DE HL M(HL) bit substring store bit(16) in DE to bit(16) in memory at HL bix08 A B D H A/HL bit index, A=source, B=search D=len(source), E=len(search) bix16 B C DE HL A/HL bit index, B=len(source), C=len(search), DE=source, HL=search boolf B DE HL HL bool(x,y,b), B=4-bit mask x,y operands in DE and HL ie12n A HL sign extend A to HL ie10n A A integer extract sign (8-bit) ie20n HL A integer extract sign (16-bit) imdop DE HL HL integer mod(x,y) iab07 A A integer 7 abs(i) iab15 HL HL integer 15 abs(i) imaxf DE HL HL integer max(x,y) iminf DE HL HL integer min(x,y) iroun HL A HL integer round(i,k) iexop HL A HL integer ** k (k pos constant) slvts HL A string load varying to stack A=length of string on return slcts A HL string load char to stack A=length of char string ssvfs A B HL string store varying from stack A=current len, B=max length sscfs A B HL string store char from stack smvvm A DE HL string move vary to vary in memory A=max target len, DE=source, HL=target smvcm A DE HL string move vary to char in memory A=target length smcvm A B DE HL string move char to vary in memory A=max target len, B=source len smccm A B DE HL A=target len, B=source len sjsts A ST ST' string juxtapose (catenate) stack A=length of left, ST=chars of left ST'=pushed psw with length of right followed by chars of right sjscm A B HL string juxtapose stack with char memory A=stacked len, B=char len, HL=.char sjsvm A HL string juxtapose stack with vary memory savvm A B HL string append vary to vary in memory A=char len, B=max target length sasvm A B HL string append stack to vary in memory A=stacked length, B=max target length sacvm A B HL string append char to vary in memory A=char len, B=max target length scccm A B DE HL string compare char to char in memory A=len right, B=len left, DE=char left, HL=char right sccvm B DE HL string compare char to vary in memory B=len left, DE=.char, HL=.vary scvcm A DE HL string compare vary to char in memory A=len right char, DE=.vary, HL=.char scvvm DE HL string compare vary to vary in memory DE=.vary left, HL=.vary right scscm A B HL string compare stack to char in memory A=len stk, B=len char, HL=.char scsvm A HL string compare stack to vary in memory A=len stk, HL=.vary sccms A B HL string compare char in mem to stack A=len stk, B=len char, HL=.char scvms A HL string compare vary in mem to stack A=Ien stk, HL=.vary scsts A string compare stack to stack A=len right element on stack, ST is stack right string, next is pushed psw with len left string, followed by left string, result: sign value & cond if 1 < r, zero value & cond if 1 = r, pos value & cond if 1 >= r, nzer value & cond if 1 > r. cs2ad A E HL char substr(ex,ei) address A=length, E=ei, HL=ex A=result length on return cs3ad A C E HL char substr(ex,ei,el) address C=el A=result length on return vs2ad E HL vary substr(ex,ei) address E=ei, HL=ex A=result length on return vs3ad C E HL vary substr(ex,ei,el) address C=el A=result length on return cxccm A B DE A/HL str index char to char in memory A=len right, B=len left, DE=char left, HL=char right cxcvm B DE A/HL str index char to vary in memory B=len left, DE=.char, HL=.vary cxvcm A DE A/HL str index vary to char in memory A=.len right char, DE=.vary, HL=.char cxvvm DE A/HL str index vary to vary in memory DE=.vary left, HL=.vary right cxscm A B A/HL str index stack to char in memory A=len stk, B=len char, HL=.char cxsvm A A/HL str index stack to vary in memory A=len stk, HL=.vary cxcms A B A/HL str index char in mem to stack A=len stk, B=len char, HL=.char cxvms A A/HL str index vary in mem to stack A=len stk, HL=.vary cxsts A str index stack to stack A=len right element on stack, ST is stack right string, next is pushed psw with len left string, followed by left string, result: A/HL=0 if right not found in left, otherwise index returned verop A ST ST A/A/HL verify(s,c), A=len(c), st has chars(c) len(s) chars(s) colop A/ST collateo, A=128, stack has xl2op A ST ST A/ST translate(s,t), A=len(t), stack has chars(t), s xl3op A ST ST ST A/ST translate(s,t,x) A=len(x), stack has chars(x), t, s 0, 1, ..., 127 (ascii chars) dldop A HL ST decimal load to stack, A=prec dasop A ST HL decimal assign, stack to memory dadop ST ST ST decimal add to stack dsuop ST ST ST decimal subtract to stack dngop ST ST decimal negate to stack dcmop ST A decimal compare operator dexop ST ST ST decimal exponentiate to stack dmuop ST ST ST decimal multiply to stack ddvop ST ST ST decimal divide to stack dsiop ST A decimal sign extract dmodf ST ST ST decimal mod(x,y) dabsf ST ST decimal abs(x) dmaxf ST ST ST decimal max(x,y) dminf ST ST ST decimal min(x,y) droun ST A ST decimal round(x,k) dtrnc ST ST decimal trunc(x) dflor ST ST decimal floor(x) dceil ST ST decimal ceil(x) dexop ST A ST decimal ** k (k pos constant) qcdop A B ST ST convert character to decimal A string length, B=scale ST character string, returns ST decimal number qddsl A ST ST decimal/decimal left shift A=shift count qddsr A ST ST decimal/decimal right shift A=shift count qicop A HL convert integer to char in stack A=string size, HL=integer value qvcop A/ST A/ST convert varying to char qi07d A ST convert fix(7) to decimal qi15d HL ST convert fix(15) to decimal qi07f A ST convert fix(7) to float qi15f HL ST convert fix(15) to float qfi07 ST A convert float to fix(7) qfi15 ST HL convert float to fix(15) qfcss A ST A/ST convert float-char stack to stack A=target length, ST=fp number qfcms A M(HL) A/ST convert float-char memory to stack qb08c A B ST convert bit(8) in a, to string in stack, with precision b qb16c HL B ST convert bit(16) in HL to string qb08i A B HL convert bit(8) in A to fixed with precision B in HL qb16i HL B HL convert bit(16) to fixed qi07b A B A convert fix(<8) to bit(8) fixed precision in b qi15b HL B HL convert fix(<16) to bit(16) qdi07 ST A convert dec in stack to fix(7) qdi15 ST HL convert dec in stack to fix(15) qciop A/ST HL convert char in stack to integer qcfop A/ST ST convert char in stack to float qccop A B ST A/ST convert char to char on stack A=len(s), B=converted length return A=b, ST trunc or extend nstop BC DE HL M(HL) non-computational store, move M(DE) to M(HL) for BC bytes nc22n DE HL A double byte non-computational compare: zero flag set if DE=HL, non-zero otherwise ncomp BC DE HL M(HL) non-computational compare, M(DE)-M(HL), set flags
5.3. Direct CP/M Function Calls
Access to all CP/M version 1 and 2 functions, and equivalent MP/M calls, is accomplished through the optional subroutines included in PLIDIO.ASM, given in the listing of Appendix A, and included in source form on the PL/I-80 diskette.
The PLIDIO.ASM subroutines are not included as a part of the standard PLILIB.IRL file because specific applications may require various changes to the direct CP/M functions which either remove operations to decrease space, or alter the manner in which the interface to a specific function takes place. Note that if the interface to a function is changed, it is imperative that the name of the entry point is also changed to avoid confusion when the program is read by another programmer.
The relocatable file, PLIDIO.REL, is created by assembling the source
program using RMAC:
rmac plidio $pz+s
(the $pz+s option avoids production of the listing and symbol files).
Given that a PL/I-80 program, such as DIOCOPY.PLI,
is present on the disk, the DIOCOPY.REL file is produced by typing:
pli diocopy
(a listing of the DIOCOPY program is given in Appendix
C). These two programs are then linked with the PLILIB.IRL file by
typing:
link diocopy,plidio
resulting in the file DIOCOPY.COM which is a program that directly
executes under CP/M.
The file DIOMOD.DCL is a source file containing
the standard PLIDIO entry point declarations so that they can be conveniently
copied into the source program during compilation using the "include" statement
%include `x:diomod.dcl';
where the optional "x:" drive prefix indicates the drive name (A:
through P:) containing the DIOMOD.DCL file. The
drive prefix need not be present if the DIOMOD.DCL file is on the same
drive as the PLI source file. The contents of the DIOMOD.DCL file is shown
below, and in the listing of Appendix C.
dcl memptr entry returns (ptr), memsiz entry returns (fixed(15)), memwds entry returns (fixed(15)), dfcb0 entry returns (ptr), dfcb1 entry returns (ptr), dbuff entry returns (ptr), reboot entry, rdcon entry returns (char(1)), wrcon entry (char(1)), rdrdr entry returns (char(1)), wrpun entry (char(1)), wrlst entry (char(1)), coninp entry returns (char(1)), conout entry (char(1)), rdstat entry returns (bit(1)), getio entry returns (bit(8)), setio entry (bit(8)), wrstr entry (ptr), rdbuf entry (ptr), break entry returns (bit(1)), vers entry returns (bit(16)), reset entry, select entry (fixed(7)), open entry (ptr) returns (fixed(7)), close entry (ptr) returns (fixed(7)), sear entry (ptr) returns (fixed(7)), searn entry returns (fixed(7)), delete entry (ptr), rdseq entry (ptr) returns (fixed(7)), wrseq entry (ptr) returns (fixed(7)), make entry (ptr) returns (fixed(7)), rename entry (ptr), logvec entry returns (bit(16)), curdsk entry returns (fixed(7)), setdma entry (ptr), allvec entry returns (ptr), wpdisk entry, rovec entry returns (bit(16)), filatt entry (ptr), getdpb entry returns (ptr), getusr entry returns (fixed(7)), setusr entry (fixed(7)), rdran entry (ptr) returns (fixed(7)), wrran entry (ptr) returns (fixed(7)), filsiz entry (ptr), setrec entry (ptr), resdrv entry (bit(16)), wrranz entry (ptr) returns (fixed(7));
Three programs are included which illustrate the use of the PLIDIO calls. Appendix B lists the DIOCALLS program that gives examples of all the basic functions, while Appendix C shows how the fundamental disk I/O operations take place, in a program called DIOCOPY which performs a fast file-to-file copy function. The last program, given in Appendix D, illustrates the operation of the random access primitives. These programs are designed to demonstrate all of the PLIDIO entry points, and show various additional PL/I-80 programming facilities in the process.
The file FCB.DCL is used throughout DIOCOPY and
DIORAND to define the body of each File Control Block declaration. This
file is copied into the source program during compilation using the statement:
%include 'x:fcb.dcl';
where, again, "x:" denotes the optional drive prefix for the drive
containing the FCB.DCL file.
Note that the use of these entry points generally precludes the use
of some PL/I-80 facilities. In particular, the dynamic storage area
is used by the PL/I-80 system for recursive procedures and file I/O
buffering. (Be aware that there are no guarantees that the dynamic storage
area will not be used for other purposes as additional facilities are added
to PL/I-80.) Thus, the use of the MEMPTR function as shown in Appendix
B disallows the use of dynamic storage allocation functions. Further,
you must ensure that the various file maintenance functions, such as delete
and rename do not access a file which is currently open in the PL/I-80
file system. Simple peripheral access, as shown in these examples, is generally
safe since no buffering takes place in this case.