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.