This section describes the layout of memory used by various Digital
Research language processors so that the programmer can properly interface
assembly language routines with high level language programs and the PL/I-80
runtime subroutine library. A set of standard subroutine interface conventions
is also given so that programs produced by various programmers and language
processors can be conveniently interfaced.

4.1. Representation of Data Elements

The internal memory representation of data items is presented below.

4.1.1. Pointers, and Entry and Label Variables

Variables which provide access to memory addresses are stored as two
contiguous bytes, with the low order byte stored first in memory. Pointer,
Entry, and Label data items appear graphically as shown below:

+----+----+| LS | MS |+----+----+

where "LS" denotes the least significant half of the address, and
"MS" denotes the most significant portion. Note that MS is the "page address,"
where each memory page is 256 bytes, and LS is the address within the page.

4.1.2. Fixed Binary Data Format

Simple single and double byte signed integer values are stored in Fixed
Binary format. Two modes are used, depending upon the precision of the
data item. Fixed Binary values with precision 1-7 are stored as single
byte values, while data items with precision 8-15 are stored in a word
(double byte) location. As with other 8080, 8085, and Z-80 items, the least
significant byte of multi-byte storage appears first in memory. All Fixed
Binary data is represented in two's complement form, allowing single byte
values in the range -128 to +127, and word values in the range -32768 to
+32767. The values 0, 1, and -1 are shown graphically below, where each
boxed value represents a byte of memory, with the low order byte appearing
before the high order byte:

Bit String data, like the Fixed Binary items shown above, are represented
in two forms, depending upon the declared precision. Bit Strings of length
1-8 are stored in a single byte, while Bit Strings of length 9-16 occupy
a word (double byte) value. Bit values are left justified in the word,
with "don't care" bits to the right when the precision is not exactly 8
or 16 bits. The least significant byte of a word value is stored first
in memory. The Bit String constant values '1'b, 'A0'b4, and '1234'b4 are
stored as shown below

Two forms of character data are stored in memory, depending upon the
declaration. Fixed character strings, declared as CHAR(n) without the VARYING
attribute, occupy n contiguous bytes of storage with the first string character
stored lowest in memory. Character strings declared with the VARYING attribute
are prefixed by the character string length, ranging from 0 to 254. The
length of the area reserved for a CHAR(n) VARYING is n+l. Note that in
either case, n cannot exceed 254. The string constant

where "10" is the (hexadecimal) string length, and "?" represents
undefined character positions.

4.1.5.Fixed Decimal Data Representation

Decimal data items are stored in packedBCD form, using nine's complement
data representation. The least significant BCD pair is stored first in
memory, with one BCD digit position reserved for the sign. Positive numbers
have a 0 sign, while negative numbers have a 9 in the high order sign digit
position. The number of bytes occupied by a decimal number depends upon
its declared precision. Given a decimal number with precision p, the number
of bytes reserved is the integer part of

(P + 2) / 2

where p varies between 1 and 15, resulting in a minimum of 1 byte
and a maximum of 8 bytes to hold a decimal data item. Given a decimal number
field of precision 5, the numbers 12345 and -2 are represented as shown
below:

+--+--+--+ +--+--+--+|45|23|01| |98|99|99|+--+--+--+ +--+--+--+

4.1.6. Floating Point Binary Representation

Floating Point Binary numbers are stored in four consecutive byte locations,
no matter what the declared precision. The number is stored with a 24 bit
mantissa, which appears first in memory, followed by an 3-bit exponent.
Following data storage conventions, the least significant byte of the mantissa
is stored first in memory. The floating point number is normalized so that
the most significant bit of the mantissa is "1" for non-zero numbers. A
zero mantissa is represented by an exponent byte of 00. Since the most
significant bit of the mantissa must be "1" for non-zero values, this bit
position is replaced by the mantissa sign. The binary exponent byte is
biased by 80 (hexadecimal) so that 81 represents an exponent of 1 while
7F represents an exponent of -1. The Floating Point Binary value 1.5 has
the representation shown below:

+--+--+--+--+|00|00|40|81|+--+--+--+--+

Note that in this case, the mantissa takes the bit stream form

0100 0000 0000 0000 0000 0000

which indicates that the mantissa sign is positive. Setting the
(assumed) high order bit to "1" produces the mantissa bit stream

1100 0000 0000 0000 0000 0000

Since the exponent 81 has a bias of 80, the binary exponent is 1,
resulting in the binary value

1100 0000 0000 0000 0000 0000

or, equivalently, 1.5 in a decimal base.

4.1.7. File Constant Representation

Each file constant in a PL/I-80 program occupies 32 contiguous bytes,
followed by a variable length field of 0 to 14 additional bytes. The fields
of a file constant are all implementation dependent and subject to change
without notice.

4.2. Layout of Aggregate Storage

PL/I-80 data items are contiguous in memory with no filler bytes. Bit
data is always stored unaligned. Arrays are stored in row-major order,
with the first subscript running slowest and the last subscript running
fastest. The RMAC COMMON statement is used to share data with PL/I-80 programs
which declare data using the EXTERNAL attribute. The following PL/I-80
program is used as an example:

Communication between high-level and assembly language routines can
be performed using the PL/I-80 general-purpose parameter passing mechanism
described below. Specifically, upon entry to a PL/I-80 or assembly language
routine, the HL register pair gives the address of a vector of pointer
values which, in turn, lead the the actual parameter values. This situation
is illustrated in the diagram below, where the address fields are assumed
as shown for this example:

The number of parameters, and the parameter length and type is determined
implicitly by agreement between the calling program and called subroutine.

Consider the following situation, for example. Suppose a PL/I-80 prog
ram uses a considerable number of floating point divide operations, where
each division is by a power of two. Suppose also that the loop where the
divisions occur is speed-critical, and thus an assembly language subroutine
will be used to perform the division. The assembly language routine will
simply decrement the binary exponent for the floating point number for
each power of two in the division, effectively performing the divide operations
without the overhead of unpacking, performing the general division operation,
and repacking the result. During the division, however, the assembly language
routine could produce underflow. Thus, the assembly language routine will
have to signal the UNDERFLOW condition if this occurs.

The programs which perform this function are given on the following
pages. The DTEST program, listed first, tests the division operation. The
external entry DIV2 is the assembly language subroutine that performs the
division, and is defined on line 4 with two parameters: a fixed(7) and
a floating point binary value. The test value 100 is stored into "f" on
each loop at line 9, and is passed to the DIV2 subroutine on line 10. Each
time DIV2 is called, the value of f is changed to f/(2**i) and printed
using a PUT statement. At the point of call, DIV2 receives a list of two
addresses, corresponding to the two parameters i and f, used in the computation.

The assembly language subroutine, called DIV2, is listed next. Upon
entry, the value of i is loaded to the accumulator, and the HL pair is
set to point to the exponent field of the input floating point number.
If the exponent is zero, DIV2 returns immediately since the resulting value
is zero. Otherwise, the subroutine loops at the label "dby2" while counting
down the exponent as the power of two diminishes to zero. If the exponent
reaches zero during this counting process, an UNDERFLOW signal is raised.

The call to "?signal" within DIV2 demonstrates the assembly language
set-up for parameters which use the general-purpose interface. The ?signal
subroutine is a part of the PL/I-80 subroutine library (PLILIB.IRL) . The
HL register pair is set to the signal parameter list, denoted by "siglst."
The signal parameter list, in turn, is a vector of four addresses which
lead to the signal code "sigcode," the signal subcode "sigsub," the file
name indicator "sigfil" (not used here), and the auxiliary message "sigaux"
which is the last parameter. The auxiliary message is used to provide additional
information to the operator when the error takes place. The signal subroutine
prints the message until either the string length is exhausted (32, in
this case) or a binary 00 is encountered in the string.

The (abbreviated) output from this test program is shown following the
assembly language listing. Note that the loop counter i becomes negative
when it reaches 128, but the processing within the DIV2 subroutine treats
this value as an unsigned magnitude value, thus the underflow occurs when
i reaches -123.

As an alternative to returning values through the parameter list, as
described in the previous section, subroutines can produce function values
which are returned directly in the registers or on the stack. This section
shows the general-purpose conventions for returning data as functional
values.

4.4.1. Returning Pointer, Entry, and Label
Variables

Variables which provide access to memory addresses occupy a word value,
as described in the previous section. In the case of Pointer, Entry, and
Label Variables, the values are returned in the HL register pair. If a
label variable is returned which can be the target of a GO TO operation,
it is the responsibility of the subroutine containing the label to restore
the stack to the proper level when control reaches the label.

4.4.2. Returning Fixed Binary Data

Functions which return Fixed Binary data items do so by leaving the
result in the A register or HL register pair, depending upon the precision
of the data item. Fixed Binary data with precision 1-7 are returned in
A, while precision 8-15 items are returned in HL. It is always safe to
return the value in HL, with the low order byte copied to the A register,
so that register A is equal to register L upon return.

4.4.3. Returning Bit String Data

Similar to Fixed Binary data items, Bit String data is returned in the
A register, or the HL register pair, depending upon the precision of the
data item. Bit Strings of length 1-8 are returned in A, while precision
9-16 items are returned in the HL pair. Note that Bit Strings are left
justified in their fields, so the BIT(1) value "true" is returned in the
A register as 80 (hexadecimal). Again, it is safe to return a bit value
in the HL register pair, with a copy of the high order byte in A, so that
register A is equal to register H upon return.

4.4.4. Returning Character Data

Character data items are returned on the stack, with the length of the
string in register A, regardless of whether the function has the VARYING
attribute. The string

where register A contains the string length 10 (hexadecimal), and
the Stack Pointer (SP) addresses the first character in the string.

4.4.5. Returning Fixed Decimal Data

Fixed Decimal data is always returned as a sixteen decimal digit value
(8 contiguous bytes) in the stack. The low order decimal pair is stored
lowest in memory (at the "top" of the stack), with the high order digit
pair highest in memory. The number is represented in nine's complement
form, and sign-extended through the high order digit position, with a positive
sign denoted by 0, and a negative sign denoted by 9. The decimal number
-2, for example, is returned as shown below:

Floating Point numbers are returned as a four-byte sequence at the top
of the stack, regardless of the declared precision. The low order byte
of the mantissa is at the top of the stack, followed by the middle byte,
then the high byte. The fourth byte is the exponent of the number. The
value 1.5 is returned as shown in the following diagram:

+--+--+--+--+|00|00|40|81| (low stack)+--+--+--+--+ ^ SP

The sequence

POP DPOP B

loads the Floating Point value from the stack for manipulation,
leaving the exponent in B, and the 24-bit mantissa in C, D, and E. The
result can be placed back into the stack using

PUSH BPUSH D

An example of returning a functional value is shown in the two program
listings which follow. The first program, called FDTEST, is similar to
the previous floating point divide test, but instead includes an entry
definition for FDIV2 which is an assembly language subroutine that returns
the result in the stack. The FDIV2 subroutine is then listed, which resembles
the previous DIV2 program with some minor changes. First note that the
input floating point value is loaded into the BCDE registers so that a
temporary copy can be manipulated which does not affect the input value.
The exponent field in register B is decremented by the input count, and
returned on the stack before the PCHL is executed.