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:
Fixed Binary(7) Fixed Binary(15)
+----+ +----+----+
| 00 | | 00 | 00 |
+----+ +----+----+Fixed Binary(7) Fixed Binary(15)
+----+ +----+----+
| 01 | | 01 | 00 |
+----+ +----+----+Fixed Binary(7) Fixed Binary(15)
+----+ +----+----+
| FE | | FE | FF |
+----+ +----+----+
4.1.3. Bit Data Representation
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
Bit(8) Bit(16)
+----+ +----+----+
| 80 | | 00 | 80 |
+----+ +----+----+Bit(8) Bit(16)
+----+ +----+----+
| AO | | 00 | AO |
+----+ +----+----+Bit(8) Bit(16)
+----+----+
N/A | 34 | 12 |
+----+----+
4.1.4. Character Data Representation
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
'Walla Walla Wash'
is stored in a CHAR(20) fixed character string as
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|W|a|l|l|a| |W|a|l|l|a| |W|a|s|h| | | | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
This same string is stored in a CHAR(20) VARYING data area as
+--+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|10|W|a|l|l|a| |W|a|l|l|a| |W|a|s|h|?|?|?|?|
+--+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
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:
declare
a (10) bit(8) external,
1 b external,
2 c bit(8),
2 d fixed binary(15),
2 e (0:2,0:1) fixed;
The following RMAC COMMON areas share data areas with the program
containing the declaration given above.
common /a/
x: ds 1
common /b/
c: ds 1
d: ds 2
e00: ds 2
e01: ds 2
e10: ds 2
e11: ds 2
e20: ds 2
e21: ds 2
where the labels e00, e01, e21 correspond to the PL/I-80 subscripted
variable locations e (0, 0), e(0, 1), . . . , e (2, 1).
4.3. General Parameter Passing Conventions
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:
HL Parm Address Actual Parameters
+------+ +------+ +----------------+
| 1000 | 1000: | 2000 | 2000: | parameter #1 |
+------+ +------+ +----------------+
| 3000 | 3000: | parameter #2 |
+------+ +----------------+
| 4000 | 4000: | parameter #3 |
+------+ +----------------+
. . . . . .
+------+ +----------------+
| 5000 | 5000: | last parameter |
+------+ +----------------+
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.
PL/I-80 V1.0, COMPILATION OF: DTEST L: List Source Program NO ERROR(S) IN PASS 1 NO ERROR(S) IN PASS 2 PL/I-80 V1.0, COMPILATION OF: DTEST 1 a 0000 dtest: 2 a 0006 proc options(main); 3 c 0006 dcl 4 c 0006 div2 entry(fixed(7),float), 5 c 0006 i fixed(7), 6 c 0006 f float; 7 c 0006 8 c 0006 do i = 0 by 1; 9 c 000A f = 100; 10 c 0015 call div2(i,f); 11 c 001B put skip list('100 / 2 **',i,'=',f); 12 c 0063 end; 13 a 0063 end dtest; CODE SIZE = 0063 DATA AREA = 0018 END COMPILATION public div2 extrn ?signal ; entry: ; p1 -> fixed(7) power of two ; p2 -> floating point number ; exit: ; p1 -> (unchanged) ; p2 -> p2 / (2**p1) div2: ;HL = .low(.p1) 0000 5E mov e,m ;low(.p1) 0001 23 inx h ;HL = .high(.p1) 0002 56 mov d,m ;DE = .p1 0003 23 inx h ;HL = .low(p2) 0004 1A ldax d ;a = p1 (power of two) 0005 5E mov e,m ;low(.p2) 0006 23 inx h ;HL = .high(.p2) 0007 56 mov d,m ;DE = .p2 0008 EB xchg ;HL = .p2 ; ; A = power of 2, HL = .low byte of fp num 0009 23 inx h ;to middle of mantissa 000A 23 inx h ;to high byte of mantissa 000B 23 inx h ;to exponent byte 000C 34 inr m 000D 35 dcr m ;p2 already zero? 000E C8 rz ;return if so dby2: ;divide by two 000F B7 ora a ;counted power of 2 to zero? 0010 C8 rz ;return if so 0011 3D dcr a ;count power of two down 0012 35 dcr m ;count exponent down 0013 C20F00 jnz dby2 ;loop again if no underflow ; ;underflow occurred, signal underflow condition 0016 210000 lxi h,siglst;signal parameter list 0019 CD0000 call ?signal ;signal underflow 001C C9 ret ;normally, no return ; dseg 0000 0800 siglst: dw sigcod ;address of signal code 0002 0900 dw sigsub ;address of subcode 0004 0A00 dw sigfil ;address of file code 0006 0C00 dw sigaux ;address of aux message ; end of parameter vector, start of params 0008 03 sigcod: db 3 ;03 = underflow 0009 80 sigsub: db 128 ;arbitrary subcode for id 000A 0000 sigfil: dw 0000 ;no associated file name 000C 0E00 sigaux: dw undmsg ;0000 if no aux message 000E 20556E6465undmsg: db 32,'Underflow in Divide by Two',0 002A end A>b:dtest 100 / 2 ** 0 = 1.000000E+02 100 / 2 ** 1 = 5.000000E+01 100 / 2 ** 2 = 2.500000E+01 100 / 2 ** 3 = 1.250000E+01 100 / 2 ** 4 = 0.625000E+01 100 / 2 ** 5 = 3.125000E+00 100 / 2 ** 6 = 1.562500E+00 100 / 2 ** 7 = 0.781250E+00 100 / 2 ** 8 = 3.906250E-01 100 / 2 ** 9 = 1.953125E-01 100 / 2 ** 10 = 0.976562E-01 100 / 2 ** 11 = 4.882812E-02 100 / 2 ** 12 = 2.441406E-02 100 / 2 ** 13 = 1.220703E-02 100 / 2 ** 14 = 0.610351E-02 100 / 2 ** 15 = 3.051757E-03 100 / 2 ** 16 = 1.525878E-03 100 / 2 ** 17 = 0.762939E-03 100 / 2 ** 18 = 3.814697E-04 100 / 2 ** 19 = 1.907348E-04 100 / 2 ** 20 = 0.953674E-04 100 / 2 ** 21 = 4.768371E-05 100 / 2 ** 22 = 2.384185E-05 100 / 2 ** 23 = 1.192092E-05 100 / 2 ** 24 = 0.596046E-05 100 / 2 ** 25 = 2.980232E-06 100 / 2 ** 26 = 1.490116E-06 100 / 2 ** 27 = 0.745058E-06 100 / 2 ** 28 = 3.725290E-07 100 / 2 ** 29 = 1.862645E-07 100 / 2 ** 30 = 0.931322E-07 100 / 2 ** 31 = 4.656612E-08 100 / 2 ** 32 = 2.328306E-08 100 / 2 ** 33 = 1.164153E-08 100 / 2 ** 34 = 0.582076E-08 100 / 2 ** 35 = 2.910383E-09 100 / 2 ** 36 = 1.455191E-09 100 / 2 ** 37 = 0.727595E-09 100 / 2 ** 38 = 3.637978E-10 100 / 2 ** 39 = 1.818989E-10 100 / 2 ** 40 = 0.909494E-10 100 / 2 ** 41 = 4.547473E-11 100 / 2 ** 42 = 2.273736E-11 100 / 2 ** 43 = 1.136868E-11 100 / 2 ** 44 = 0.568434E-11 100 / 2 ** 45 = 2.842170E-12 100 / 2 ** 46 = 1.421085E-12 100 / 2 ** 47 = 0.710542E-12 100 / 2 ** 48 = 3.552713E-13 100 / 2 ** 49 = 1.776356E-13 100 / 2 ** 50 = 0.888178E-13 100 / 2 ** 51 = 4.440892E-14 100 / 2 ** 52 = 2.220446E-14 100 / 2 ** 53 = 1.110223E-14 100 / 2 ** 54 = 0.555111E-14 100 / 2 ** 55 = 2.775557E-15 100 / 2 ** 56 = 1.387778E-15 100 / 2 ** 57 = 0.693889E-15 100 / 2 ** 58 = 3.469446E-16 100 / 2 ** 59 = 1.734723E-16 100 / 2 ** 60 = 0.867361E-16 100 / 2 ** 61 = 4.336808E-17 100 / 2 ** 62 = 2.168404E-17 100 / 2 ** 63 = 1.084202E-17 100 / 2 ** 64 = 0.542101E-17 100 / 2 ** 65 = 2.710505E-18 100 / 2 ** 66 = 1.355252E-18 100 / 2 ** 67 = 0.677626E-18 100 / 2 ** 68 = 3.388131E-19 100 / 2 ** 69 = 1.694065E-19 100 / 2 ** 70 = 0.847032E-19 100 / 2 ** 71 = 4.235164E-20 100 / 2 ** 72 = 2.117582E-20 100 / 2 ** 73 = 1.058791E-20 100 / 2 ** 74 = 0.529395E-20 100 / 2 ** 75 = 2.646977E-21 100 / 2 ** 76 = 1.323488E-21 100 / 2 ** 77 = 0.661744E-21 100 / 2 ** 78 = 3.308722E-22 100 / 2 ** 79 = 1.654361E-22 100 / 2 ** 80 = 0.827180E-22 100 / 2 ** 81 = 4.135903E-23 100 / 2 ** 82 = 2.067951E-23 100 / 2 ** 83 = 1.033975E-23 100 / 2 ** 84 = 0.516987E-23 100 / 2 ** 85 = 2.584939E-24 100 / 2 ** 86 = 1.292469E-24 100 / 2 ** 87 = 0.646234E-24 100 / 2 ** 88 = 3.231174E-25 100 / 2 ** 89 = 1.615587E-25 100 / 2 ** 90 = 0.807793E-25 100 / 2 ** 91 = 4.038967E-26 100 / 2 ** 92 = 2.019483E-26 100 / 2 ** 93 = 1.009741E-26 100 / 2 ** 94 = 0.504871E-26 100 / 2 ** 95 = 2.524354E-27 100 / 2 ** 96 = 1.262177E-27 100 / 2 ** 97 = 0.631088E-27 100 / 2 ** 98 = 3.155443E-28 100 / 2 ** 99 = 1.577721E-28 100 / 2 ** 100 = 0.788860E-28 100 / 2 ** 101 = 3.944304E-29 100 / 2 ** 102 = 1.972152E-29 100 / 2 ** 103 = 0.986076E-29 100 / 2 ** 104 = 4.930380E-30 100 / 2 ** 105 = 2.465190E-30 100 / 2 ** 106 = 1.232595E-30 100 / 2 ** 107 = 0.616297E-30 100 / 2 ** 108 = 3.081487E-31 100 / 2 ** 109 = 1.540743E-31 100 / 2 ** 110 = 0.770372E-31 100 / 2 ** 111 = 3.851859E-32 100 / 2 ** 112 = 1.925929E-32 100 / 2 ** 113 = 0.962964E-32 100 / 2 ** 114 = 4.814824E-33 100 / 2 ** 115 = 2.407412E-33 100 / 2 ** 116 = 1.203706E-33 100 / 2 ** 117 = 0.601853E-33 100 / 2 ** 118 = 3.009265E-34 100 / 2 ** 119 = 1.504632E-34 100 / 2 ** 120 = 0.752316E-34 100 / 2 ** 121 = 3.761581E-35 100 / 2 ** 122 = 1.880790E-35 100 / 2 ** 123 = 0.940395E-35 100 / 2 ** 124 = 4.701977E-36 100 / 2 ** 125 = 2.350988E-36 100 / 2 ** 126 = 1.175494E-36 100 / 2 ** 127 = 0.587747E-36 100 / 2 ** -128 = 2.938735E-37 100 / 2 ** -127 = 1.469367E-37 100 / 2 ** -126 = 0.734683E-37 100 / 2 ** -125 = 3.673419E-38 100 / 2 ** -124 = 1.836709E-38 100 / 2 ** -123 = 0.918354E-38 100 / 2 ** -122 = 4.591774E-39 UNDERFLOW (128), Underflow in Divide by Two Traceback: 017F 011B End of Execution
4.4. Returning Values from Functions
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
'Walla Walla Wash'
for example, is returned as shown in the diagram below:
+--+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
A |10| |W|a|l|l|a| |W|a|l|l|a| |W|a|s|h| (low stack)
+--+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
^
SP
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:
+--+--+--+--+--+--+--+--+
|98|99|99|99|99|99|99|99| (low stack)
+--+--+--+--+--+--+--+--+
^
SP
4.4.6. Returning Floating Point Numbers
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:
+--+--+--+--+The sequence
|00|00|40|81| (low stack)
+--+--+--+--+
^
SP
POP D
POP 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 B
PUSH 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.
PL/I-80 V1.0, COMPILATION OF: FDTEST L: List Source Program NO ERROR(S) IN PASS 1 NO ERROR(S) IN PASS 2 PL/I-80 V1.0, COMPILATION OF: FDTEST 1 a 0000 dtest: 2 a 0006 proc options(main); 3 c 0006 dcl 4 c 0006 fdiv2 entry(fixed(7),float) 5 c 0006 returns(float), 6 c 0006 i fixed(7), 7 c 0006 f float; 8 c 0006 9 c 0006 do i = 0 by 1; 10 c 000A put skip list('100 / 2 **',i,'=', 11 c 0055 fdiv2(i,100)); 12 c 0055 end; 13 a 0055 end dtest; CODE SIZE = 0055 DATA AREA = 0018 END COMPILATION public fdiv2 extrn ?signal ; entry: ; p1 -> fixed(7) power of two ; p2 -> floating point number ; exit: ; p1 -> (unchanged) ; p2 -> (unchanged) ; stack: p2 / (2 ** p1) fdiv2: ;HL = .low(.p1) 0000 5E mov e,m ;low(.p1) 0001 23 inx h ;HL = .high(.p1) 0002 56 mov d,m ;DE = .p1 0003 23 inx h ;HL = .low(p2) 0004 1A ldax d ;a = p1 (power of two) 0005 5E mov e,m ;low(.p2) 0006 23 inx h ;HL = .high(.p2) 0007 56 mov d,m ;DE = .p2 0008 EB xchg ;HL = .p2 ; ; A = power of 2, HL = .low byte of fp num 0009 5E mov e,m ;E = low mantissa 000A 23 inx h ;to middle of mantissa 000B 56 mov d,m ;D = middle mantissa 000C 23 inx h ;to high byte of mantissa 000D 4E mov c,m ;C = high mantissa 000E 23 inx h ;to exponent byte 000F 46 mov b,m ;B = exponent 0010 04 inr b ;B = 00? 0011 05 dcr b ;becomes 00 if so 0012 CA2A00 jz fdret ;to return from float div dby2: ;divide by two 0015 B7 ora a ;counted power of 2 to zero? 0016 CA2A00 jz fdret ;return if so 0019 3D dcr a ;count power of two down 001A 05 dcr b ;count exponent down 001B C21500 jnz dby2 ;loop again if no underflow ; ;underflow occurred, signal underflow condition 001E 210000 lxi h,siglst;signal parameter list 0021 CD0000 call ?signal ;signal underflow 0024 010000 lxi b,0 ;clear to zero 0027 110000 lxi d,0 ;for default return ; 002A E1 fdret: pop h ;recall return address 002B C5 push b ;save high order fp num 002C D5 push d ;save low order fp num 002D E9 pchl ;return to calling routine ; dseg 0000 0800 siglst: dw sigcod ;address of signal code 0002 0900 dw sigsub ;address of subcode 0004 0A00 dw sigfil ;address of file code 0006 0C00 dw sigaux ;address of aux message ; end of parameter vector, start of params 0008 03 sigcod: db 3 ;03 = underflow 0009 80 sigsub: db 128 ;arbitrary subcode for id 000A 0000 sigfil: dw 0000 ;no associated file name 000C 0E00 sigaux: dw undmsg ;0000 if no aux message 000E 20556E6465undmsg: db 32,'Underflow in Divide by Two',0 002A end