home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Simtel MSDOS 1992 September
/
Simtel20_Sept92.cdr
/
msdos
/
c
/
apm.arc
/
FUNCTION
< prev
next >
Wrap
Text File
|
1988-10-09
|
16KB
|
451 lines
APM
apmInit(init, scale_factor, base)
long init;
int scale_factor;
short base;
{}
This routine initializes a new APM value. The 'init' parameter is a long
integer that represents its initial value, the 'scale_factor' variable
indicates how this initial value should be scaled, and 'base' is the base of
the initial value. Note that the APM value returned by this routine is
normally a reclaimed APM value that has been previously disposed of via
apmDispose(); only if there are no previous values to be reclaimed will this
routine allocate a fresh APM value (see also the apmGarbageCollect()
routine).
Bases can be 2 - 36, 10000, or 0, where 0 defaults to base 10000.
If the call fails, it will return (APM)NULL and 'apm_errno' will contain a
meaningful result. Otherwise, a new APM value will be initialized.
For example, assume that we want to initialize two APM values in base 10000,
the first to 1.23456 and the second to 1 E20 ("one times 10 to the 20th
power"):
APM apm_1 = apmInit(123456L, -5, 0);
APM apm_2 = apmInit(1L, 20, 0);
As a convenience, the following macro is defined in apm.h:
#define apmNew(BASE) apmInit(0L, 0, (BASE))
int
apmDispose(apm)
APM apm;
{}
This routine disposes of a APM value 'apm' by returning it to the list of
unused APM values (see also the apmGarbageCollect() routine). It returns
an appropriate status which is also put into 'apm_errno'.
int
apmGarbageCollect()
{}
When APM values are disposed of, they remain allocated. Subsequent calls to
apmInit() may then return a previously allocated but disposed APM value.
This is done for speed considerations, but after a while there may be lots of
these unused APM values lying around. This routine reclaims the space taken
up by these unused APM values (it frees them). It returns an appropriate
status which is also put into 'apm_errno'.
int
apmAdd(result, apm1, apm2)
APM result;
APM apm1;
APM apm2;
{}
This routine adds 'apm1' and 'apm2', putting the sum into 'result', whose
previous value is destroyed. Note that all three parameters must have been
previously initialized via apmInit().
The 'result' parameter cannot be one of the other APM parameters.
The return code and the 'apm_error' variable reflect the status of this
function.
int
apmSubtract(result, apm1, apm2)
APM result;
APM apm1;
APM apm2;
{}
This routine subtracts 'apm2' from 'apm1', putting the difference into
'result', whose previous value is destroyed. Note that all three parameters
must have been previously initialized via apmInit().
The 'result' parameter cannot be one of the other APM parameters.
The return code and the 'apm_errno' variable reflect the status of this
function.
int
apmMultiply(result, apm1, apm2)
APM result;
APM apm1;
APM apm2;
{}
This routine multiplies 'apm1' and 'apm2', putting the product into 'result',
whose previous value is destroyed. Note that all three parameters must have
been previously initialized via apmInit().
The 'result' parameter cannot be one of the other APM parameters.
The return code and the 'apm_errno' variable reflect the status of this
function.
int
apmDivide(quotient, radix_places, remainder, apm1, apm2)
APM quotient;
int radix_places;
APM remainder;
APM apm1;
APM apm2;
{}
This routine divides 'apm1' by 'apm2', producing the 'quotient' and
'remainder' variables. Unlike the other three basic operations,
division cannot be counted on to produce non-repeating decimals, so
the 'radix_places' variable exists to tell this routine how many
digits to the right of the radix point are to be calculated before
stopping. If the 'remainder' variable is set to (APM)NULL, no
remainder is calculated ... this saves quite a bit of computation time
and hence is recommended whenever possible.
All APM values must have been previously initialized via apmInit() (except,
of course the 'remainder' value if it is to be set to NULL).
Division by zero creates a zero result and a warning.
The 'quotient' and 'remainder' variables can't be one of the other APM
parameters.
The return code and the 'apm_errno' variable reflect the status of this
function.
int
apmCompare(apm1, apm2)
APM apm1;
APM apm2;
{}
This routine compares 'apm1' and 'apm2', returning -1 if 'apm1' is less than
'apm2', 1 if 'apm1' is greater than 'apm2', and 0 if they are equal.
It is not an error if 'apm1' and 'apm2' are identical, and in this case the
return value is 0.
The 'apm_errno' variable contains the error code. You must check this value:
if it is set to an error indication, the comparison failed and the return
value is therefore meaningless.
int
apmCompareLong(apm, longval, scale_factor, base)
APM apm;
long longval;
int scale_factor;
short base;
{}
This routine works just like apmCompare(), but it compares the 'apm' value to
'longval', scaled by 'scale_factor' in 'base'. The 'apm_errno' variable
contains the error code.
int
apmSign(apm)
APM apm;
{}
This routine returns the sign of the 'apm' value: -1 for negative, 1 for
positive. The 'apm_errno' variable contains the error code. You must check
'apm_errno': if it's non-zero, the function return value is meaningless.
int
apmAbsoluteValue(result, apm)
APM result;
APM apm;
{}
This routine puts the absolute value of 'apm' into 'result', whose previous
value is destroyed. Note that the two parameters must have been previously
initialized via apmInit().
The 'result' parameter cannot be the other APM parameter.
The return code and the 'apm_errno' variable reflect the status of this
function.
int
apmNegate(result, apm)
APM result;
APM num;
{}
This routine puts the additive inverse of 'apm' into 'result', whose previous
value is destroyed. Note that the two parameters must have been previously
initialized via apmInit().
The 'result' parameter cannot be the other APM parameter.
The return code and the 'apm_errno' variable reflect the status of this
function.
int
apmReciprocal(result, radix_places, apm)
APM result;
int radix_places;
APM num;
{}
This routine puts the multiplicative inverse of 'apm' into 'result', whose
previous value is destroyed. Note that the two APM parameters must have been
previously initialized via apmInit(). Since taking the reciprocal involves
doing a division, the 'radix_places' parameter is needed here for the same
reason it's needed in the apmDivide() routine.
Taking the reciprocal of zero yields zero with a warning status.
The 'result' parameter cannot be the other APM parameter.
The return code and the 'apm_errno' variable reflect the status of this
function.
int
apmScale(result, apm, scale_factor)
APM result;
APM apm;
int scale_factor;
{}
This routine assigns to 'result' the value of 'apm' with its radix point
shifted by 'scale_factor' (positive 'scale_factor' means shift left). The
'scale_factor' represents how many places the radix is shifted in the base of
'apm' unless 'apm' is in base 10000 ... in this special case, 'scale_factor'
is treated as if the base were 10.
This is a very quick and accurate way to multiply or divide by a power of 10
(or the number's base).
The 'result' parameter cannot be the other APM parameter.
The return code and the 'apm_errno' variable reflect the status of this
function.
int
apmValidate(apm)
APM apm;
{}
This routine sets 'apm_errno' and its return status to some non-zero value if
'apm' is not a valid APM value.
int
apmAssign(result, apm)
APM result;
APM num;
{}
This routine assigns the value of 'apm' to 'result', whose previous value is
destroyed. Note that the two parameters must have been previously
initialized via apmInit().
It is not considered an error if 'result' and 'apm' are identical; this case
is a virtual no-op.
The return code and the 'apm_errno' variable reflect the status of this
function.
int
apmAssignLong(result, long_value, scale_factor, base)
APM result;
long long_value;
int scale_factor;
short base;
{}
This routine assigns a long int to 'result'. Its second through fourth
parameters correspond exactly to the parameters of apmInit(). The only
difference between the two routines is that this one requires that its result
be previously initialized. The 'long_value' parameter is a long that
represents the value to assign to 'result', the 'scale_factor' variable
indicates how this value should be scaled, and 'base' is the base of the
value.
Bases can be 2 - 36, 10000, or 0, where 0 defaults to base 10000.
For example, assume that we want to assign values to two previously
initialized APM entities, apm_1 and apm_2. The base will be base 10000, the
first value will be set to 1.23456 and the second will be set to 1 E20 ("one
times 10 to the 20th power"):
int ercode;
ercode = apmAssignLong(apm_1, 123456L, -5, 0);
...
ercode = apmAssignLong(apm_2, 1L, 20, 0);
...
The return code and the 'apm_errno' variable reflect the status of this
function.
int
apmAssignString(apm, string, base)
APM apm;
char *string;
short base;
{}
This routine takes a character string containing the ASCII representation of
a numeric value and converts it into a APM value in the base specified. The
'apm' parameter must have been previously initialized, 'string' must be
non-NULL and valid in the specified base, and 'base' must be a valid base.
The return code and the 'apm_errno' variable reflect the status of this
function.
int
apmConvert(string, length, decimals, round, leftjustify, apm)
char *string;
int length;
int decimals;
int round;
int leftjustify;
APM apm;
{}
This routine converts a APM value 'apm' into its ASCII representation
'string'. The 'length' parameter is the maximum size of the string (including
the trailing null), the 'decimals' parameter is the number of decimal places
to display, the 'round' parameter is a true-false value which determines
whether rounding is to take place (0 = false = no rounding), the
'leftjustify' parameter is a true-false value which determines whether the
result is to be left justified (0 = false = right justify; non-zero = true =
left justify), and the 'apm' paramter is the APM value to be converted.
The 'string' parameter must point to an area that can hold at least 'length'
bytes.
If the 'decimals' parameter is < 0, the string will contain the number of
decimal places that are inherent in the APM value passed in.
The return code and the 'apm_errno' variable reflect the status of this
function.
int
(*apmErrorFunc(newfunc))()
int (*newfunc)();
{}
This routine registers an error handler for errors and warnings. Before any
of the other APM routines return to the caller, an optional error handler
specified in 'newfunc' can be called to intercept the result of the
operation. With a registered error handler, the caller can dispense with the
repetitious code for checking 'apm_errno' or the function return status after
each call to a APM routine.
If no error handler is registered or if 'newfunc' is set to NULL, no action
will be taken on errors and warnings except to set the 'apm_errno' variable.
If there is an error handler, it is called as follows when there is an error
or a warning:
retcode = (*newfunc)(ercode, message, file, line, function)
where ...
int retcode; /* returned by 'newfunc': should be 'ercode' */
int ercode; /* error code */
char *message; /* a short string describing the error */
char *file; /* the file in which the error occurred */
int line; /* the line on which the error occurred */
char *function; /* the name of the function in error */
Note that your error handler should normally return 'ercode' unless it does a
longjmp, calls exit(), or in some other way interrupts the normal processing
flow. The value returned from your error handler is the value that the apm
routine in error will return to its caller.
The error handler is called after 'apm_errno' is set.
This routine returns a pointer to the previously registered error handler or
NULL if one isn't registered.
int
apmCalc(result, operand, ..., NULL)
APM result;
APM operand, ...;
{}
This routine performs a series of calculations in an RPN ("Reverse
Polish Notation") fashion, returning the final result in the 'result'
variable. It takes a variable number of arguments and hence the
rightmost argument must be a NULL.
Each 'operand' is either a APM value or a special constant indicating
the operation that is to be performed (see below). This routine makes
use of a stack (16 levels deep) similar to that in many pocket
calculators. It also is able to access a set of 16 auxiliary
registers (numbered 0 through 15) for holding intermediate values.
The stack gets reinitialized at the start of this routine, so values
that have been left on the stack from a previous call will disappear.
However, the auxiliary registers are static and values remain in these
registers for the duration of your program. They may also be
retrieved outside of this routine (see the apmGetRegister() and
apmSetRegister() routines, below).
An operand that is an APM value is automatically pushed onto the stack
simply by naming it in the function call. If the stack is full when a
value is being pushed onto it, the bottommost value drops off the
stack and the push succeeds; this is similar to how many pocket
calculators work. Also, if the stack is empty, a pop will succeed,
yielding a zero value and keeping the stack empty. The topmost value
on the stack is automatically popped into the 'result' parameter after
all the operations have been performed.
An operand that is one of the following special values will cause
an operation to be performed. These operations are described in the
following list. Note that the values "V", "V1", and "V2" are used
in the following list to stand for temporary values:
APM_ABS pop V, push absolute value of V
APM_NEG pop V, push -V
APM_CLEAR empty the stack
APM_DUP pop V, push V, push V
APM_SWAP pop V1, pop V2, push V1, push V2
APM_SCALE(N) pop V, push V scaled by N [ as in apmScale() ]
APM_PUSH(N) V = value in register N, push V
APM_POP(N) pop V, store it in register N
APM_ADD pop V1, pop V2, push (V2 + V1)
APM_SUB pop V1, pop V2, push (V2 - V1)
APM_MUL pop V1, pop V2, push (V2 * V1)
APM_DIV(N) pop V1, pop V2, push (V2 / V1) with N radix places
[ as in apmDivide() ], remainder goes into register 0
APM_RECIP(N) pop V, push 1/V with N radix places
[ as in apmReciprocal() ]
Since register 0 is used to hold the remainder in a division, it is
recommended that this register not be used to hold other values.
As an example, assume that APM values "foo", "bar", and "baz" have
been initialized via apmInit() and that "foo" and "bar" are to be used
to calculate "baz" as follows (assume that divisions stop after 16
decimal places have been calcluated):
baz = 1 / ((((foo * bar) + foo) / bar) - foo)
The function call will be:
bcdCalc(baz, foo, APM_DUP, APM_POP(1), bar, APM_DUP, APM_POP(2),
APM_MUL, APM_PUSH(1), APM_ADD, APM_PUSH(2), APM_DIV(16),
APM_PUSH(1), APM_SUB, APM_RECIP(16), NULL);
Note that the value of "foo" is stored in register 1 and the value of
"bar" is stored in register 2. After this call, these registers will
still contain those values.
int
apmGetRegister(regvalue, regnumber)
APM regvalue;
int regnumber;
{}
The value in auxiliary register number 'regnumber' is assigned to APM
value 'regvalue'. The 'regnumber' parameter must be between 0 and 15,
inclusive. The 'regvalue' parameter must have been previously
initialized via apmInit().
int
apmSetRegister(regvalue, regnumber, newvalue)
APM regvalue;
int regnumber;
APM newvalue;
{}
The value in auxiliary register number 'regnumber' is assigned to APM
value 'regvalue', and then the APM value 'newvalue' is stored in that
same register. The 'regnumber' parameter must be between 0 and 15,
inclusive. The 'regvalue' and 'newvalue' parameters must have been
previously initialized via apmInit().