 |
top_estack |
Variable (ROM Call 0x109) |
Points to the top of the expression stack.
The global variable top_estack points to the top (i.e. the last byte) of the expression stack.
Strictly speaking, in "nostub" mode it is not a real variable but smart macro, although it
works like it is a variable. The expression stack is the place in the memory where TI keeps
expressions during evaluation. All statements are tokenized 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 languages. 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 expression stack.
The actual processing of all expressions is done via the expression stack. The position in the
stack is given by top_estack. Pushing a value appends it to the expression 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
expression stack. Expressions are therefore interpreted from the top (high memory) recursively.
As an artefact of expressions processing mechanism, the arguments of the called program are
kept also on the expression stack. It grows upwards, and the last argument is stored first.
After the assembly program is called, the image of the expression 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 expression 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 expression 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 underscore 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 on 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 illustrated with
some examples:
Algebraic form: |
integrate (e^(x^2), x, 1, 2) |
RPN form: |
2 1 x 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 subtractions 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 explicit 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 canonic 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.
See MULTI_EXPR.
If everything mentioned above is not so clear for you, compile and run the following example
(called "Print EStack"):
// Print bottom_estack and top_estack
#define USE_TI89 // Compile for TI-89
#define USE_TI92PLUS // Compile for TI-92 Plus
#define USE_V200 // Compile for V200
#define MIN_AMS 100 // Compile for AMS 1.00 or higher
#define SAVE_SCREEN // Save/Restore LCD Contents
#include <tigcclib.h> // Include All Header Files
// Main Function
void _main(void)
{
ClrScr ();
printf_xy (0, 40, "Bottom = 0x%lp", bottom_estack);
printf_xy (0, 50, "Top = 0x%lp", 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 at the addresses
shown, to see how parameters are stored.
Used by: can_be_approxed, check_estack_size, compare_complex_magnitudes, delete_between, deleted_between, did_push_cnvrt_Float_to_integer, index_below_display_expression_aux, is_antisymmetric, is_symmetric, map_tail, map_tail_Int, next_expression_index, NG_graphESI, Parse2DExpr, Parse2DMultiExpr, push_between, push_expr_quantum, push_expr2_quantum, push_Float, push_Float_to_nonneg_int, push_Float_to_rat, push_internal_simplify, push_offset_array, push_parse_text, push_quantum, push_quantum_pair, push_round_Float, push_transpose_aux, push_ushort_to_integer, reset_estack_size, should_and_did_push_approx_arg2, TokenizeSymName, HeapAllocESTACK, ArgCount, EX_getArg, InitArgPtr, cmd_blddata, cmd_circle, cmd_custom, cmd_dialog, cmd_disp, cmd_drawfunc, cmd_drawinv, cmd_drawparm, cmd_drawpol, cmd_endfor, cmd_endwhile, cmd_fill, cmd_for, cmd_get, cmd_if, cmd_ifthen, cmd_input, cmd_inputstr, cmd_linetan, cmd_local, cmd_newdata, cmd_newplot, cmd_output, cmd_passerr, cmd_pause, cmd_popup, cmd_prompt, cmd_randseed, cmd_request, cmd_shade, cmd_sinreg, cmd_slpline, cmd_sorta, cmd_sortd, cmd_toolbar, cmd_try, cmd_while, did_push_anti_deriv, did_push_series, push_1st_derivative, push_abs, push_acos, push_acosh, push_asin, push_asinh, push_atan, push_atanh, push_ceiling, push_coldim, push_colnorm, push_comb, push_comdenom, push_conj, push_cosh, push_cross_product, push_csolve, push_cumsum, push_czeros, push_def_int, push_denominator, push_desolve, push_determinant, push_diag, push_dimension, push_dotproduct, push_eigvc, push_eigvl, push_exp, push_expand, push_extended_prod, push_factor, push_floor, push_fractional_part, push_gcd_numbers, push_im, push_integer_lcm, push_integer_part, push_integer_quotient, push_integer_remainder, push_is_prime, push_left, push_lim, push_list_to_mat, push_ln, push_log10, push_matnorm, push_max, push_max1, push_max2, push_mean, push_median, push_mid, push_min, push_min1, push_min2, push_mod, push_mrowadd, push_nint, push_nsolve, push_nth_derivative, push_numerator, push_part, push_perm, push_phase, push_pttest, push_pxltest, push_r_cis, push_rand, push_randmat, push_randnorm, push_randpoly, push_re, push_rec_to_angle, push_red_row_ech, push_right, push_rotate, push_round, push_row_echelon, push_rowadd, push_rowdim, push_rownorm, push_rowswap, push_sequence, push_shift, push_sign, push_simult, push_sin2, push_sinh, push_solve, push_stddev, push_str_to_expr, push_string, push_submat, push_summation, push_switch, push_tan, push_tanh, push_unitv, push_variance, push_when, push_zeros, did_push_to_polar, push_and, push_assignment, push_degrees, push_dot_exponentiate, push_equals, push_exponentiate, push_factorial, push_greater_than, push_greater_than_or_equals, push_indir_name, push_less_than, push_less_than_or_equals, push_list_plus, push_list_times, push_matrix_product, push_negate, push_not, push_not_equals, push_or, push_product, push_radians, push_ratio, push_substitute_no_simplify, push_substitute_simplify, push_substitute_using_such_that, push_sum, push_to_cylin, push_to_sphere, VarNew, VarOpen, VarSaveAs, EV_defaultHandler, handleRclKey, handleVarLinkKey, GR3_paint3d, GR3_xyToWindow, HomeExecute, HomePushEStack, HS_popEStack, getcalc, OSLinkCmd, sendcalc, atof, add_to_top, and_onto_top, are_units_consistent, compare_numbers, cpt_gr_fun, cpt_gr_param, cpt_gr_polar, de_initRes, de_loop, did_map_aggregate_arg, did_push_approx_inflection_point, did_push_divide_units, did_push_lincf, divide_top, dv_create_graph_titles, EQU_getNameInfo, ForceFloat, get_lb, get_list_indices, get_matrix_indices, get_ub, GetStatValue, GM_Derivative, GM_DistArc, GM_Inflection, GM_Integrate, GM_Intersect, GM_Math1, GM_TanLine, gr_ck_solvergraph, gr_execute_de, gr_execute_seq, GR_Pan, GR3_addContours, GraphOrTableCmd, GT_CalcDepVals, GT_PrintCursor, has_different_variable, index_if_pushed_binomial_info, index_if_pushed_qquad_info, InitDEMem, InitTimeSeq, is_equivalent_to, is_negative, is_never0, is_nonnegative, is_nonpositive, is_positive, is_real, is_term_improper, or_onto_top, push_auto_units_conversion, push_but_factor, push_but_term, push_constant_factors, push_constant_terms, push_dependent_factors, push_dependent_terms, push_div_dif_1c, push_div_dif_1f, push_float_qr_fact, push_format, push_gcd_then_cofactors, push_independent_factors, push_independent_terms, push_lu_fact, push_make_proper, push_minus_recip_of_quantum, push_mrow_aux, push_negate_quantum_as_negint, push_nonconstant_factors, push_nonconstant_terms, push_nonnumeric_factors, push_parse_prgm_or_func_text, push_pi_on_quantum, push_poly_deg_in_var_or_kernel, push_poly_qr, push_quantum_as_nonnegative_int, push_quantum_pair_as_pos_frac, push_reciprocal, push_reciprocal_of_quantum, push_simplify, push_simplify_statements, push_sq_matrix_to_whole_number, push_standardize, push_symbolic_qr_fact, push_trig, push_user_func, push_var, push_zero_partial_column, raise_to_top, Regraph, replace_top_with_post_simplified, replace_top_with_reciprocal, replace_top2_with_and, replace_top2_with_imre, replace_top2_with_or, replace_top2_with_pow, replace_top2_with_prod, replace_top2_with_ratio, replace_top2_with_sum, run_one_seq, seqWebInit, setup_unit_system, SP_Define, spike_geo_titles, spike_in_editor, spike_optionD, spike_titles_in_editor, store_func_def, store_to_subscripted_element, subtract_from_top, time_loop, times_top, tokenize_if_TI_92_or_text, TokenizeName, VarStoreLink, FolderDel, FolderRename, HSymDel, SymFindFolderName, VarRecall, ROM Call 0x45B, ROM Call 0x468, ROM Call 0x46F, ROM Call 0x47F, ROM Call 0x480, ROM Call 0x484, ROM Call 0x485, ROM Call 0x48D, ROM Call 0x494, ROM Call 0x495, ROM Call 0x4C2, ROM Call 0x4C6, ROM Call 0x4C7, ROM Call 0x4D1, ROM Call 0x4E6, ROM Call 0x4EC, ROM Call 0x4ED, ROM Call 0x4F2, ROM Call 0x5F1, ROM Call 0x606
See also: bottom_estack