The <args.h> header file
This header file contains the following functions:
ArgCount EX_getArg EX_getBCD GetArgType
GetFloatArg GetIntArg GetStrnArg GetSymstrArg
InitArgPtr RemainingArgCnt SkipArg
the following global variables:
top_estack
and the following predefined types:
bcd Bool ESI Tags
ti_float
NOTE: The header file estack.h contains much more powerful set of
functions for manipulating with expressions stack.
Functions
Returns a number of arguments passed to the called program.
ArgCount returns a number of arguments passed to the called program. It is a simple
macro which calls TIOS function remaining_element_count.
See also RemainingArgCnt.
void InitArgPtr (ESI &ap);
Initializes a pointer to the first argument passed to the called program.
InitArgPtr is a macro which initializes ap (which is a pointer of type
ESI) to point to the first argument passed to the assembly
program. Principally, calling
InitArgPtr (argptr);
is equal to doing
argptr = top_estack;
See top_estack for more info.
InitArgPtr must be used before the first call to GetStrnArg etc.
Here is an example of the program which reads string or integer arguments passed to it,
and displays them on the screen, one by one:
#include <tigcclib.h>
int _ti89;
void _main(void)
{
ESI argptr;
int argtype;
long num;
InitArgPtr (argptr);
while ((argtype = GetArgType(argptr)) != END_TAG)
{
DrawStr(0, 30, " ", A_REPLACE);
if (argtype == STR_TAG)
DrawStr (0, 30, GetStrnArg (argptr), A_REPLACE);
else if (argtype == POSINT_TAG || argtype == NEGINT_TAG)
{
num = GetIntArg (argptr);
if (argtype == NEGINT_TAG) num = -num;
printf_xy (0, 30, "%ld", num);
}
else
{
DrawStr (0, 30, "Wrong arg type!", A_REPLACE);
ngetchx ();
break;
}
ngetchx ();
}
}
If the name of this program is example.89z, try to call it on TI using
example ("strarg1", 123, -12, "strarg2")
to see how it works in practice.
NOTE: I used notation "&ap" in the prototype description, although passing by
reference does not exist in ordinary C (only in C++). However, this macro is implemented
on such way that it simulates "passing by reference".
unsigned char GetArgType (ESI ap);
Returns the type of the current argument.
GetArgType returns an one-byte value which determines the type of the current argument
(pointed to by ap). There are so many types of arguments: strings, integers
(positive or negative), fractions, floats, symbols, various algebraic expressions,
lists, matrices, etc. Enum Tags
defines a lot of constants: the most commonly used in argument lists are
STR_TAG, POSINT_TAG, NEGINT_TAG, FLOAT_TAG, POSFRAC_TAG, ENDFRAC_TAG,
COMPLEX_TAG, LIST_TAG and END_TAG. They represents respectively a string, a positive integer, a negative
integer, a floating point value, a positive fraction, a negative fraction, a complex number,
a begining of the list structure and an end-of-list marker (which also has meaning "no more arguments").
Any values except these need to be regarded as "an expression": you need to handle them
"by hand" (of course, if you know how).
See InitArgPtr for an example of usage.
NOTE: You can use SkipArg to bypass the argument which you don't know
how to handle (or, which you don't want to handle for any reason).
void SkipArg (ESI &ap);
Skips the current argument.
SkipArg is a macro which modifies ap to point to the next argument in
the argument list, regardless of its type. Note that this function must not be
called if GetArgType returns END_TAG,
i.e. if there is no more arguments. Else, an error will be thrown.
NOTE: SkipArg is implemented by calling TIOS function
next_expression_index.
char *GetStrnArg (ESI &ap);
Returns the current argument of string type.
GetStrnArg is a macro which returns the current string argument
(pointed to by ap) and modifies ap to point to the next argument in
the argument list. So, each successive time GetStrnArg is used, it returns the next
argument in the argument list.
See InitArgPtr for an example of usage.
Note that GetStrnArg assumes that the current argument IS a string (this may be
checked using GetArgType). If this is not true, the
result of GetStrnArg is unpredictable.
unsigned long GetIntArg (ESI &ap);
Returns the current argument of integer type.
GetIntArg is a macro which returns the current integer argument
(pointed to by ap) and modifies ap to point to the next argument in
the argument list. So, each successive time GetIntArg is used, it returns the next
argument in the argument list. If the argument is a negative number (check the sign using
GetArgType), GetIntArg returns its absolute value.
See InitArgPtr for an example of usage.
Note that GetIntArg assumes that the current argument IS an integer, either positive
or negative (this may be checked using GetArgType). If this
is not true, the result of GetIntArg is unpredictable.
If the current argument type is a fraction, do the following to pick it:
numerator = GetIntArg (ap);
ap++;
denominator = GetIntArg (ap);
i.e. pick two integers, with increasing argument pointer between two picks.
NOTE: It is not recommended to do something like
a = GetIntArg (top_estack);
It works fine sometimes, but not always. GetIntArg
is a function-looking macro, with changes the value of its actual
argument. So, if you write the statement mentioned above,
you will also change the value of TIOS system variable top_estack,
which is not always a good idea. So, I strictly recommend using an auxilary variable,
like in the following example:
ESI argptr = top_estack;
...
a = GetIntArg (argptr);
Using this method, you will avoid unexpected changes of top_estack.
float GetFloatArg (ESI &ap);
Returns the current argument of floating point type.
GetFloatArg is a macro which returns the current floating point argument
(pointed to by ap) and modifies ap to point to the next argument in
the argument list. So, each successive time GetFloatArg is used, it returns the next
argument in the argument list.
Note that GetFloatArg assumes that the current argument IS a floating point value (not an
integer). This may be checked using GetArgType. If this
is not true, the result of GetFloatArg is unpredictable. For more flexibility, see also
estack_number_to_Float from
estack.h header file.
char *GetSymstrArg (ESI &ap);
Returns a pointer to the terminating zero byte of the current argument of string type.
GetSymstrArg does the same task as GetStrnArg but returns
a pointer to the terminating zero byte of the string, instead of a pointer to the
first byte of the string. This function is implemented because nearly all functions for
TIOS VAT handling need just the pointer to the terminating byte of the string. As
the arguments are stored in memory as strings which are bounded with zero bytes from
both sides, the result of GetSymstrArg may be directly passed to TIOS VAT routines.
See vat.h for more info.
unsigned short RemainingArgCnt (ESI ap);
Returns a remaining number of arguments passed to the called program.
RemainingArgCnt returns a number of remaining arguments passed to the called program,
i.e. a number of arguments which are not yet picked up. It is a simple
macro which calls TIOS function remaining_element_count.
See also ArgCount.
ESI EX_getArg (short n);
Returns a pointer to the n-th argument.
EX_getArg is TIOS function which may be useful if you want to access arguments in
non_sequential order. EX_getArg returns a pointer to the argument which ordinal
number is n (which may be later processed using GetIntArg
etc.). Arguments are numbered from zero (i.e. 0, 1, 2, ... N-1
where N is the total number of arguments). Note that when n = N,
EX_getArg returns NULL, and when n > N,
EX_getArg throws an error, so it is good idea to check in advance the total number
of arguments using ArgCount.
short EX_getBCD (short n, float *dest);
Gets the n-th floating point argument.
EX_getBCD is a somewhat limited TIOS function. It first calls EX_getArg
passing n to it. Then, if the argument pointed to by the result of
EX_getArg is not a floating point number, EX_getBCD returns
FALSE, else stores the floating point value to the variable pointed to by
dest and returns TRUE.
Global variables
ESI top_estack;
The global variable top_estack points to the top (i.e. the last byte) of the expressions stack.
Strictly speaking, in "nostub" mode it is not a real variable but smart macro, although it
works like it is a variable. Expressions stack is the place in the memory where TI keeps
expressions during the evaluation. All statements are tokenised before being executed
(interpreted). Instructions are reduced to (byte sized) quanta and parsed into Reverse
Polish Notation (RPN). This is a common technique in interpreted languagues. Expressions,
functions, etc. are all stored in RPN form, to allow for efficient operation of the expressions
stack. See below for more details about organization of the expressions stack.
The actual processing of all expressions is done via the expressions stack. The position in the
stack is given by top_estack. Pushing a value appends it to the expressions stack and increments
top_estack. When a expression is interpreted, expressions are reduced, and executed as far as
possible. Whatever remains on the stack is the result, this may then be stored or processed
later.
When a file is interpreted the end of the variable is found and it is processed as a separate
expression stack. It is then processed recursively, producing a simplified version on the real
expressions stack. Expressions are therefore interpreted from the top (high memory) recursively.
As an artefact of expressions processing mechhanism, the arguments of the called program are
kept also on the expressions stack. It grows upwards, and the last argument is stored first.
After the assembly program is called, the image of the expressions stack is as follows:
- The first byte (i.e. the byte with the lowest address) on the stack is END_TAG (0xE5).
- Then, a argument list follows, starting from the last argument up to the first
argument. So, top_estack points to the last byte of the first argument.
Each string entry on the expressions stack is stored as follows (from lower to higher addresses):
- One zero byte (starting tag);
- Content of the string;
- Terminating zero byte;
- Byte STR_TAG (0x2D).
Each integer entry is stored as follows:
- Bytes of the number in little endian format (i.e. the lowest byte is stored first);
the number of bytes varies depending of the size of the number;
- A byte which represents the number of the bytes which made the number
(note that zero has zero-byte length);
- Byte POSINT_TAG (0x1F) or NEGINT_TAG (0x20), depending whether the number is
positive or negative (for negative numbers, previous bytes contain the absolute
value).
Each fraction entry is stored as follows:
- Bytes of the denominator in little endian format (i.e. the lowest byte is stored first);
the number of bytes varies depending of the size of the number;
- A byte which represents the number of the bytes which made the denominator;
- Bytes of the numerator in little endian format;
- A byte which represents the number of the bytes which made the numerator;
- Byte POSFRAC_TAG (0x21) or NEGFRAC_TAG (0x22), depending whether the fraction is
positive or negative (for negative fractions, previous bytes contain absolute
values).
Each floating point entry is stored as follows:
- Only first 9 bytes of the content of the floating point value
are stored (instead of 10), because TIOS always rounds floating point values up to 14
significant digits before pushing them on expressions stack
(see bcd structure for more information about the internal
organization of floating point values);
- The last byte is FLOAT_TAG (0x23).
If the entry is complex number, real part is stored first (which can be integer, float,
fraction, etc.), then imaginary part. COMPLEX_TAG (0x9D) is stored after them, so if the
current argument type is complex, decrease the argument pointer by one, then first read
imaginary part, then real part separately.
If the entry is composite (i.e. if it is a list or a matrix), the first byte is end_of_list marker
(byte END_TAG or 0xE5), then follow each element of the list in reverse order (starting
from the last element), and the last byte is LIST_TAG (0xD9).
Now, you surely have an idea how you can pick up elements from the list. Note that a matrix is
a "list of lists".
Signed zeros (POSITIVE_ZERO and
NEGATIVE_ZERO) are represented as
fractions +0/1 and -0/1.
Variable names are stored exactly like strings without terminating STR_TAG (i.e. it is a
sequence of letters bounded with two zero bytes), so "variable" tag is just a zero byte.
There is an exception: one-letter variables have unique one-byte tags (see
Tags for more info. Also, note that variable names are always
stored with lowercase letters. Variables whose names ends with an underscore are assumed
to be complex, and variables whose names starts with an understore are assumed to
represent physical units.
Expressions are stored in RPN (Reverse Polish Notation) form (also known as postfix form).
So, function calls like func(arg1,arg2,...argn) are stored as sequence
argn ... arg2 arg1 func_tag
and terms like arg1 operator arg2 are stored as
arg1 arg2 operator_tag or
arg2 arg1 operator_tag, depending of the concrete operator.
The situation is analogous for unary operators.
Note that a "pointer to an expression" is a pointer to the last byte of the expression!
When a function (or operator) has variable number of arguments, END_TAG is used to indicate "no more arguments".
See Tags for complete list of various tags. This will be ilustrated with
some examples:
Algebraic form: |
integrate (e^(x^2), x, 1, 2) |
RPN form: |
2 1 2 x ^ e ^ integrate |
Sequence of bytes: |
[02 01 1F] [01 01 1F] [08] [02 01 1F] [08] [93] [25] [93] [C4]
|
Algebraic form: |
sum (sqrt (1 + x), x, 0, a) |
RPN form: |
a 0 x x 1 sqrt sum |
Sequence of bytes: |
[0B] [00 1F] [08] [08] [01 01 1F] [8B] [51]
|
Algebraic form: |
a + b + a - b + (a + b) * (a - b) -> a |
RPN form: |
a b + a + b - a b + a b - * + a -> |
Sequence of bytes: |
[0B] [0C] [8B] [0B] [8B] [0C] [8D] [0B] [0C] [8B] [0B] [0C] [8D] [8F] [8B] [0B] [80]
|
Algebraic form: |
{{1, 2}, {3, 4}} |
RPN form: |
END_TAG END_TAG 4 3 LIST_TAG END_TAG 2 1 LIST_TAG LIST_TAG |
Sequence of bytes: |
[E5] [E5] [04 01 1F] [03 01 1F] [D9] [E5] [02 01 1F] [01 01 1F] [D9] [D9]
|
Algebraic form: |
my_func (a, b, c) |
RPN form: |
END_TAG c b a my_func USERFUNC_TAG |
Sequence of bytes: |
[E5] [0D] [0C] [0B] [00 6D 79 5F 66 75 6E 63 00] [DA]
|
To perform some algebraic transformations on more unique way, expressions should be
transformed into an equivalent form called "internal canonic form". In such form,
for example, all constants are always in front of variables, e.g. 'x*3'
and
'x+3'
becomes '3*x'
and '3+x'
(although
the second example will be printed as 'x+3'
). Also, expressions like 'x/y'
or
'x-y'
in internal canonic form do not contain substractions and divisions.
As the parameter list (when the program is called from TI-Basic) is always in internal
canonic form, such expressions will never be observed as-is in parameter lists
etc. because they will be converted before passing them to the program. A lot of functions
for algebraic manipulations automatically convert the expression in the internal canonic form,
but when this is not true, you can always force the conversion using explicite call to
push_internal_simplify function. Note that the reverse conversion
(i.e. back from the canonic form into a regular form) is performed anytime when you try to
print out the expression. Here is the list of the most common transformations which appears
during the transformation into the internal canonic form:
Expression | Standard canonnic form |
-x | (-1)*x |
x-y | x+y*(-1) |
x/y | x*y^(-1) |
e^x | exp(x) |
x^y | exp(ln(x)*y) [ except when "y" is an integer or a fraction ] |
e | exp(1) |
sqrt(x) | x^(1/2) |
log(x) | ln(x)*(ln(10)^(-1)) |
sin(x) | trig(x,0) [ assuming "radian" mode; for "trig" function, see SINCOS_TAG ] |
cos(x) | trig(x,1) |
tan(x) | trig(x,0)*trig(x,1)^(-1) |
sinh(x) | exp(x)*(1/2)+exp(x)^(-1)*(-1/2) |
cosh(x) | exp(x)*(1/2)+exp(x)^(-1)*(1/2) |
tanh(x) | (exp(x)^2+1)^(-1)*(exp(x)^2+(-1)) |
x xor y | (not x and y) or (x and not y) |
Mode dependent calculations are performed by converting expressions to a specific format,
i.e. for trigonometric functions all values are converted to radians before passing them to
radian trigonometric functions.
A variable may consist of multiple expressions, these are separated by several
special quanta: NEXTEXPR_TAG and NEWLINE_TAG.
The last expression is marked with ENDSTACK_TAG.
If everything mentioned above is not so clear for you, compile and run the following program:
#include <tigcclib.h>
int _ti89;
void _main(void)
{
printf_xy (0, 50, "Top=%lx", top_estack);
ngetchx ();
}
Run this program in VTI and pass to it parameters as you want. top_estack will be shown
on the screen. During waiting for a keypress, enter the debugger and look the addresses
below shown address, to see how parameters are stored.
Predefined types
ESI is "expressions stack index" which is, in fact, the pointer to the tag byte on the
TIOS expressions stack. It is defined as
typedef char *ESI;
See top_estack and Tags for more info about
the expressions stack.
Bool is enumerated type for describing true or false values. It is defined as
enum Bool {FALSE, TRUE};
Tags is enumerated type for describing types of entries on the expressions stack.
This enum is very big, as there is a lot of various entries (see
top_estack for more info about how entries on the
expressions stack are organized). The complete list of tags with their meanings
is given in the documentation for estack.h header file
(click here to see the list).
Starting for TIGCC 0.9, ti_float
is an alias name for standard ANSI float
type, introduced to keep backward compatibility with previous releases of the TIGCC compiler,
which don't support standard ANSI floats. See bcd for more info about
internal organization of floating point values.
bcd
is a structure which represents the internal structure of floating point values.
It is defined as
typedef struct
{
unsigned short exponent;
unsigned long long mantissa;
} ti_float;
Note that long long is not a typing error: it is a
GNU C extension for representing very long integers (8-byte integers in this implementation).
See description of bcd type in the documentation
for timath.h header file for more info about the
internal organization of floating point values.