Table and Y= variables
The table subsystem stores equations from Y=, reads settings from TBLSET, evaluates selected equations row by row, caches their values, and paints the TABLE grid. This page covers automatic and prompted independent and dependent values as well as split graph-table mode.
The subsystem shares equation storage with Graphing, parser entry points with TI-BASIC execution, VAT objects with Variables and the VAT, and text output with Display and LCD.
Subsystem components
flowchart TB
TBLSET["TBLSET screen · page 37 + 02<br/>TblMin 92B3 / TblStep 92BC<br/>tblFlags IY+19: autoFill / autoCalc / reTable"]
YEQ["Y= equations in VAT<br/>EquObj tokens · tY1..tY0"]
PARSER["parser · page 38<br/>_ParseInp / parse_eval_expr"]
subgraph GEN["TABLE generator · page 05 — per X row"]
direction TB
S1["1 · X = TblMin + k·TblStep"]
S2["2 · running-X staged in OP registers"]
S3["3 · evaluate each selected Y= → OP1"]
S4["4 · format OP1 → cell string"]
S5["5 · write into table data cache"]
S6["6 · paint cache as text grid on LCD"]
S1 --> S2 --> S3 --> S4 --> S5 --> S6
end
TBLSET -->|settings| S1
YEQ -->|_Find_Parse_Formula| S3
PARSER --> S3
The TABLE feature reuses the Y= storage and the same page-38
recursive-descent evaluator the grapher and homescreen use; it adds only
(a) the running-X driver from
TblMin/TblStep, (b) a RAM value cache so scrolling doesn’t recompute, and
(c) a text-grid renderer. [confirmed]
TABLE settings
System variables (RAM TIFloats) [confirmed]
| Addr | Name | Meaning | Token |
|---|---|---|---|
0x92B3 | TblMin (a.k.a. TblStart) | first independent value in the table | tTblMin/TBLMINt = 0x1A |
0x92BC | TblStep (ΔTbl) | increment between successive rows | tTblStep/TBLSTEPt = 0x21 |
Both are 9-byte floats. They are ordinary system token variables: read/written
through _RclSysTok (38:683E) / _StoSysTok (38:623B) using the token bytes
above (the page-38 system-var token table lives around 38:61F1). ΔTbl’s token
is the list-step token 0x21; TblStart uses 0x1A. [confirmed]
Mode flags — tblFlags (IY+19 = IY+0x13) [confirmed]
From ti83plus.inc and verified by the bit-ops below:
| Bit | Name | Meaning |
|---|---|---|
4 (0x10) | autoFill | Indpnt: 0 = Auto (fill X from TblStart/ΔTbl), 1 = Ask (prompt for each X) |
5 (0x20) | autoCalc | Depend: 0 = Auto (compute Y immediately), 1 = Ask (compute a cell only on request) |
6 (0x40) | reTable | 0 = cached table valid, 1 = must recompute the table |
TBLSET key and edit handler [confirmed]
The page-02 command/mode handler that backs the TBLSET screen edits the two
floats and the two mode rows. A retired helper label at 02:7B31 is not a live function in the current DB, but the byte sequence there decodes as:
02:7B31 RES 4,(IY+0x13) ; default Indpnt = Auto (autoFill=0)
02:7B35 SET 6,(IY+0x13) ; reTable = 1 → table is now dirty
RET
02:7B3A BIT 4,(IY+0x13) … ; toggle helpers for the menu rows:
SET 4,(IY+0x13) ; Indpnt = Ask
RES 5,(IY+0x13) ; Depend = Auto
SET 5,(IY+0x13) ; Depend = Ask
So changing any TBLSET field (TblStart, ΔTbl, Indpnt, or Depend) sets
reTable, forcing a full recompute next time the TABLE is shown. [confirmed]
TBLSET display and validation context [confirmed]
The TABLE-setup screen logic lives on page 37 (the menu/editor display page).
37:5F10 reconciles the on-screen Indpnt/Depend selection against the stored
tblFlags: it compares tblFlags bit4 (autoFill) vs a UI-selection bit
(IY+0x16 & 0x40) and bit5 (autoCalc) vs IY+0x11 & 0x40, and when either
differs it sets reTable (SET 6,(IY+0x13)). It also zeroes the table-top
row index 0x91E0 when Indpnt flips to Ask. Companion sites: 37:5F2B
(BIT 5 autoCalc), 37:5F59/37:5F94 (re-reads). [confirmed]
Y= equation storage, selection, and style
Storage [confirmed]
Y1…Y9, Y0 are system equation variables, VAT objects of type EquObj = 3
(ti83plus.inc: EquObj EQU 3; NewEquObj=0x0B, UnknownEquObj=0x0A). Each holds
word size + size bytes of the tokenized formula you typed after Y1= — the
same token encoding the homescreen and program editor use (see sub-tibasic.md).
The equation name in OP1 is the 2-byte token sequence tVarEqu (0x5E) + the
Y-token:
| Var | Token | Var | Token | |
|---|---|---|---|---|
Y1 | 0x10 | Y6 | 0x15 | |
Y2 | 0x11 | Y7 | 0x16 | |
Y3 | 0x12 | Y8 | 0x17 | |
Y4 | 0x13 | Y9 | 0x18 | |
Y5 | 0x14 | Y0 | 0x19 |
(Parametric X1T/Y1T=0x20/0x21…, polar r1…, and u/v/w sequences share the
same EquObj/tVarEqu machinery.) [confirmed]
Selection and style flags [confirmed]
Each equation’s flags byte is 0x23 when selected (plotted / tabulated) and
0x03 when deselected — i.e. the selection bit is bit 5 (0x20). The separate
per-equation style byte encodes the line style: 0=line, 1=thick, 2=shade above,
3=shade below, 4=trace/path, 5=animate, 6=dotted. The TABLE iterates the
same selected set the grapher plots, so deselecting Y2 in the Y= editor (or
clearing its = highlight) removes its column from the table. curGStyle
(0x8D17) holds the in-progress style; sGrFlags bit g_style_active
(IY+20 bit5) enables per-equation styles. The graphing doc covers the plot side; the
table only reads the selection bit to decide which columns exist. [confirmed]
The values also match the
TI link-protocol var guide.
Indexed pointer helpers — iMathPtr4 (0x84D9) [confirmed]
Two official bcalls address a RAM array of two-byte values based at iMathPtr4
(0x84D9). A third official bcall sorts a caller-supplied range:
| bcall | Addr | Role |
|---|---|---|
_PUT_INDEX_LST | 33:7066 | store a value in slot n at 0x84D9 + 2n |
_GET_INDEX_LST | 33:707A | load the value in slot n through _LdHLind |
_HEAP_SORT | 33:7097 | sort an indexed caller-supplied range |
The helper behavior is [confirmed], but the bodies do not identify what every
caller stores in the array. _HEAP_SORT does not discover selected equations.
The builder and consumer for the TABLE editor’s selected-Y set remain
[hypothesis].
Resolving and evaluating a Y-var [confirmed]
_Find_Parse_Formula (bcall ID 4AF2h) is the universal “find a named var and
parse/evaluate its stored formula” entry in TI-BASIC expression evaluation. For a Y-var it
_FindSyms the EquObj, points the parse cursor at its token body, and runs the
page-38 evaluator, leaving the result in OP1. The 38:758A entry seen here is a
thin RST2 bcall trampoline; the body switches on var type (Window 0x0F /
ZSto 0x10 / TblRng 0x11 special-cased) before the cross-page parse — i.e.
the table range is itself handled as a special “formula” type by this resolver.
It is bcalled from 03:67C0 (the Y= equation editor) and 33:7720 (graph setup).
Homescreen Y1(2) evaluates through this same path: the parser sees tVarEqu tY1,
resolves the EquObj, substitutes the argument as X, and evaluates. [confirmed]
Table generation
Page 0x05 is the TABLE editor / Graph-Table subsystem. All references to
TblMin/TblStep and to the table column-data pointers (XOutDat 0x918E,
YOutDat 0x9192) concentrate on page 05, and the page’s tblFlags bit-ops
(reTable, autoFill, autoCalc) drive the recompute/scroll logic. The TABLE
editor is installed as a context (cxTableEditor = 0x4A, ti83plus.inc),
selected from [2nd][GRAPH] via the key→context router (11-boot-contexts);
its handler vectors run on page 05.
Editor main display — table_editor_main (05:5D0D) [confirmed]
if (tblFlags & 0x40 /* reTable */) recompute = table_recompute() ; 05:5DD7
else use_cache = table_use_cache() ; 05:78CF
if (graphFlags & 1) { redraw helpers ... } ; split-graph case
paint_grid(...) ; 05:7771
So on every entry to the TABLE the editor checks reTable: if dirty it runs the
recompute driver, otherwise it repaints from the cached values. [confirmed]
Recompute driver — table_recompute (05:5DD7) [confirmed]
05:5DD7 XOR A
LD (0x8E63),A ; reset table column/row state
CALL 0x3411
RET Z ; window/mode gate
CALL 0x7704 ; init column descriptors (see below)
CALL 0x774B ; seed running-X = TblMin
CALL 0x65D2
CALL 0x65C8 ; clear per-column flags
CALL 0x5EE1 ; FILL the value cache (the row loop)
CALL 0x6014
CALL 0x5FFC ; lay out / size the columns
RES 6,(IY+0x13) ; reTable = 0 (cache now valid)
CALL 0x76BA
After a successful recompute it clears reTable, so subsequent scrolls reuse
the cache until something marks it dirty again. [confirmed]
Seeding the independent value [confirmed]
05:774B initialises the two-float table_x_work array. It first clears
table_x_work[0] (0x8622), then at 05:7751 copies TblMin into
table_x_work[1], the running-X slot:
05:7751 LD HL,0x92B3 ; TblMin
LD DE,0x862B ; running-X destination
JP 0x1A92
i.e. the first row’s independent value is TblStart. The row index is bounds-checked at
05:65DC by comparing the current row with the last row:
LD A,(0x91E0)
LD HL,CurTableRow
CP (HL)
RET
The per-row X is computed as TblStart + k·TblStep rather than by an
incremental add:
05:65DC LD A,(0x91E0)
LD HL,0x91DC
CP (HL)
RET ; row-index bound check
05:6359 LD A,(0x91DD) … LD DE,0x9221 / 0x91E2 (cell buffers)
LD HL,(0x91DC /* row idx */)
ADD
CALL _LdHLind
ADD HL,DE
So row $k$ uses $X=\mathrm{TblMin}+k\cdot\mathrm{TblStep}$. (In Indpnt = Ask mode this driver is bypassed and the user types each X; see Auto and Ask modes.) [confirmed]
Per-row evaluation [confirmed]
For each visible row the recompute fills the cache:
- Store the row’s X value in a cache slot and stage it through OP1/OP2. The
running X remains in OP registers and FPS slots rather than passing through
_StoXfor each row. - Evaluate each selected equation against the current X through bcall ID
4741h. Its body at35:7C7Cdrives the page38parser cluster:parse_init(38:5B7B),fps_alloc_to_9652(38:5B10),38:5ADA, and the_ParseInpregion at38:5987. The result remains in OP1. - Format OP1 and store the result in the row’s cache slot.
The fill driver at 05:6205 loads B = 7 for the visible rows and increments
CurTableRow (0x91DC) on each iteration. It pushes a cleanup handler through
ram:27DA, calls the evaluator once per selected equation, and stores results
through 05:6284 and 05:629B. A headless TilEm trace of Y1=X² with default
TBLSET executes the _ParseInp region seven times, once per row. Between
consecutive rows, execution passes through parse_init and fps_alloc_to_9652.
The trace does not execute _StoX (38:62A3) during the fill. [confirmed]
The cache-clearing preamble is table_fill_cache_loop (05:5EE1): it strides
table_value_cache.band[0] at 0x91E2 in 9-byte (TIFloat) steps for up to 7
visible columns (LD C,0x07), keyed off the top-row index 0x91E0. The X
column itself is written from the running-X; the Y columns from the evaluated
OP1. [confirmed]
Value cache and scrolling [confirmed]
The table keeps the visible window of computed values in a RAM cache so that scrolling is instant (no recompute):
| Addr | Role |
|---|---|
0x918C XOutSym / 0x918E XOutDat | X column: symbol + data pointer |
0x9190 YOutSym / 0x9192 YOutDat | active Y column: symbol + data pointer |
0x9194 inputSym / 0x9196 inputDat | the “Ask”/input column descriptor |
0x9198 prevData | previous-column data pointer |
0x91DB | unnamed Ask-mode row state |
0x91DC / 0x91DD | CurTableRow / CurTableCol |
0x91E0 | table-top state; exact role remains open |
0x91E2 | table_value_cache, the per-cell computed-value bands |
The three contiguous 63-byte regions at 0x91E2, 0x9221, and 0x9260
form one typed cache:
typedef struct {
TIFloat value[7];
} TableCacheBand; /* 0x3F bytes */
typedef struct {
TableCacheBand band[3];
} TableValueCache; /* 0xBD bytes at table_value_cache */
Thus 0x9221 is table_value_cache.band[1], and 0x9260 is
table_value_cache.band[2]. This notation captures both the 9-byte element
stride and the 63-byte scroll-copy stride. [confirmed]
05:6014 performs the scroll:
LD HL,0x9221
LD DE,0x9260
LDIR
This copies
table_value_cache.band[1] to band[2] (a 0x3F-byte block) and performs an
LDDR shift of a 0xB4-byte region. When the cursor moves above or below the
cached window, it slides the cache and computes only the one new row
(or recomputes if reTable). [confirmed]
Auto and Ask modes [confirmed]
05:6D40/05:6D51 read the mode bits to branch:
05:6D40 … CALL 0x74BE
JR NZ
BIT 4,(IY+0x13)
RET ; Indpnt (autoFill) test
… CALL 0x74BE
JR NZ
BIT 5,(IY+0x13)
RET ; Depend (autoCalc) test
LD A,(0x91DB) … LD A,(0x91DC) … ; Ask-mode row state
- Indpnt = Auto (bit4=0): the driver auto-fills X from TblStart/ΔTbl as described under Seeding the independent value.
- Indpnt = Ask (bit4=1): the X column starts empty. The per-row prompt body
at
05:6DFFcalls the Indpnt test at05:6D4Cand invokes the entry-line editor at05:7303. The editor pushes continuation05:7329onto the OPS stack throughram:27DAand enters setup at05:5F64and05:5F51. On success,05:6032shifts thetable_value_cacheband and enters row evaluation at05:615C. [confirmed] - Depend = Auto (bit5=0): Y cells compute immediately during the fill.
- Depend = Ask (bit5=1): the gate at
05:6DD1tests bit 5 through05:6D67and05:6D56. In Ask mode,05:69D2checks cell state at0x91CEand0x8D1B, then calls05:637Cfor one deferred evaluation. That routine pushes continuation05:644Eonto the OPS stack throughram:27DAand runs the cell expression through the standard OPS machinery. [confirmed]
The mode tests at 05:6D4C and 05:6D56 first call 05:74BE. A nonzero result
bypasses the (IY+0x13) bit tests. This override behavior is [confirmed], but
the condition detected by 05:74BE remains [hypothesis].
Grid rendering [confirmed]
The table is a text grid (not the pixel graph buffer): up to 8 visible rows ×
columns, drawn with the large font through the home-screen text primitives
(_PutMap/_PutC, display-lcd.md). The paint loop:
05:7E45 loop over visible rows:
CALL 0x7E7C ; position/clear the cell (selects buffer
; 0x9221 for one column or 0x91E2 for the other)
CALL 0x7E9D
LD DE,(0x9192 YOutDat) ; the Y-column value pointer
CP 2 / CP 5 ; column-kind dispatch (X col vs Y col vs input)
CALL 0x7E7C
CALL 0x7E98 ; render the cached value into the cell
CALL 0x65DC ; row-index bound check (current row vs last)
INC row
…
LD (0x91DC),A
05:7E7C chooses the destination cache band (table_value_cache.band[1] at
0x9221 versus band[0] at 0x91E2) based on the
column index, and writes 0xFF/blank sentinels for empty (Ask) cells. The bottom
status line and the highlighted-cell full-precision readout reuse the same value
cache. [confirmed]
Split graph-table mode [confirmed]
The G-T mode (graph on the left half, table on the right) is set up by
screen_split (bcall 0x5227): it checks the split flag, calls _Bit_VertSplit,
then 05:7544 and the table-init 05:773F (seed running-X from TblMin), and
cross-jumps to redraw. G-T mode shares the table cache and running-X
driver, rendered into the right columns alongside the plot. [confirmed]
Table invalidation through reTable [confirmed]
Anything that could change a tabulated value sets tblFlags bit6, forcing the
next TABLE view to recompute:
| Site | Trigger |
|---|---|
02:7B35 bytes | editing TblStart/ΔTbl/Indpnt/Depend in TBLSET |
37:5F3D | toggling Indpnt or Depend on the setup screen |
38:6340, 38:4809, 38:54CD | the parser storing into a Y= equation or a relevant var (editing Y1=…, →Y1, or changing X/window) |
boot / reset (RAM clear) | initialises the table as dirty (reTable set); the exact init site is not pinned here (00:4105 is the “Resetting All…” message string, not the setter) |
Conversely only the recompute driver clears it (05:5DD7, 05:62FD,
05:64DE → RES 6,(IY+0x13)). [confirmed]
End-to-end example: tabulating Y1=X² + 1
- Y=: types
X²+1afterY1=. The editor tokenizes it and stores the bytes as theEquObjY1(token5E 10) in the VAT, with its flags byte’s select bit set (the=is highlighted). The parser store path setsreTable. - TBLSET (
2nd WINDOW): setsTblStart=0(TblMin0x92B3),ΔTbl=1(TblStep0x92BC),Indpnt:Auto,Depend:Auto. Each edit setsreTable(the setup bytes around02:7B35). - TABLE (
2nd GRAPH): enters contextcxTableEditor(0x4A) on page 05.table_editor_main(05:5D0D) seesreTable=1→table_recompute(05:5DD7):- seed running-X ←
TblMin(05:774B), - walk the selected equation set — here only
Y1; the exact builder and iterator remain open, - per row: stage the running-X through OP1/OP2, evaluate
Y1’s tokens via bcall ID4741h→35:7C7Cand the page38parser cluster (_Find_Parse_Formula/_ParseInp) → OP1 =X²+1, format and stash intotable_value_cache.band[0]/band[1], - advance to the next row (bound-checked at
05:65DC; X =TblStart + k·TblStep) and repeat, - clear
reTable.
- seed running-X ←
- The grid paints (
05:7E45) the cachedXandY1columns as large-font text; scrolling (05:6014) slides the cache and computes only newly exposed rows. - Deselecting
Y1(or editing the formula, or changingΔTbl) setsreTableagain and the next view recomputes.
Routine and state index
; --- TABLE setup settings & flags ---
RAM 92B3 TblMin / TblStart ; first independent value (sys float)
RAM 92BC TblStep / ΔTbl ; row increment (sys float)
IY+19 b4 tblFlags.autoFill = Indpnt Auto/Ask
IY+19 b5 tblFlags.autoCalc = Depend Auto/Ask
IY+19 b6 tblFlags.reTable = table-dirty
02:7b20 tblsetup_handler ; TBLSET key/edit handler
02:7b35 (retired label; no live function in the current Ghidra DB)
37:5f10 tblset_cx_display ; TBLSET screen reconcile → reTable
; --- TABLE editor / generator (page 05) ---
05:5d0d table_editor_main ; reTable? recompute : use cache; paint
05:5dd7 table_recompute ; seed X, fill cache, clear reTable
05:774b table_seed_runX_from_TblMin ; runningX(0x862B) ← TblMin
05:773f table_seed_runX_from_TblMin2 ; same, split-graph path
05:65dc table_row_bound ; row-index bound check (91E0 vs (91DC))
05:5ee1 table_fill_cache_loop ; fill table_value_cache.band[0]
05:6014 table_scroll_cache ; slide cell cache on scroll (LDIR/LDDR)
05:6d40 table_mode_test ; BIT autoFill/autoCalc (Auto vs Ask)
05:7e45 table_paint_grid_loop ; render cached cells as text columns
05:7e7c table_cell_select_buffer ; pick cache band 1/0
05:7712 screen_split ; Graph-Table split-screen setup
05:62fd table_recompute_clear_reTable ; another RES6 recompute exit
; --- table value-cache RAM ---
RAM 918C/918E XOutSym / XOutDat ; X-column symbol + data ptr
RAM 9190/9192 YOutSym / YOutDat ; Y-column symbol + data ptr
RAM 9194/9196 inputSym / inputDat ; Ask-input column descriptor
RAM 9198 prevData ; previous-column data ptr
RAM 91DC/91DD CurTableRow / CurTableCol
RAM 91E0 table-top state; exact role remains open
RAM 91E2 table_value_cache (three bands × seven TIFloats)
RAM 8622 table_x_work[2]; running independent-value scratch
; --- Y= equations, selected list, evaluation ---
EquObj = 3 (VAT type) ; Y1..Y0 stored as tokenized formulas
tokens: tVarEqu=0x5E + tY1=0x10 … tY0=0x19 ; Y-var name encoding
RAM 84D9 iMathPtr4 ; indexed-list base; contents depend on caller
33:7097 _HEAP_SORT ; sort caller-supplied indexed range
33:707a _GET_INDEX_LST ; fetch slot n from 0x84D9+2n
33:7066 _PUT_INDEX_LST ; store slot n at 0x84D9+2n
38:758a _Find_Parse_Formula ; FindSym Y-var + parse its formula → OP1
38:5987 _ParseInp ; parse/eval a formula against current X
38:62a3 _StoX ; store OP1 → X system var (not on the fill path)
35:7c7c equation-eval dispatcher ; bcall 0x4741 target: per-row Y evaluation
38:67ae _RclX / 38:67a4 _RclY / 38:626c _StoY
33:5023 _GetVarVersion ; classify extended tokens by version tier
; --- reTable (dirty) setters ---
38:6340 / 38:4809 / 38:54cd parser sets reTable on Y=/var edit
(boot/RAM-clear) sets reTable (init site not pinned; 00:4105 is a message string)
Evidence summary and open items
- TblMin/TblStep addresses + tokens, the
tblFlagsbit layout, and which sites set/clearreTable: [confirmed] (equates + byte-verified bit-ops). - Page 05 = TABLE subsystem, the recompute→clear-reTable structure, the running-X
seed from TblMin and
+TblStepadvance, the cell-cache buffers, the scroll (LDIR/LDDR), and the text-grid paint loop: [confirmed] from byte disassembly; the dense Z80 bodies don’t fully reduce in the decompiler but the CALL/buffer structure is byte-pinned. - The per-row driver
05:6205(seven-row loop, bcall ID4741h→35:7C7Cequation dispatcher → page-38 parser cluster) and the once-per-row_ParseInpexecution are [confirmed] by a headless TilEm trace ofY1=X²._StoXdoes not execute during the fill; the running X moves through OP registers and FPS slots._PUT_INDEX_LST,_GET_INDEX_LST, and_HEAP_SORTare generic indexed-list helpers; their bodies do not prove that TABLE usesiMathPtr4for its selected equations. - Y= selection bit (
0x20) — flags byte0x23selected /0x03deselected — and thestylebyte values (0=line …6=dotted) are [confirmed] against the TI link-protocol var guide. - Ask-mode prompting flow is [confirmed]. Indpnt=Ask prompts through the
entry-line editor at
05:7303, with OPS continuation05:7329. Depend=Ask evaluates individual cells at05:637C, with OPS continuation05:644E. See Auto and Ask modes. _Find_Parse_Formula’sTblRng(type 0x11) special-case is [confirmed] at two byte sites:38:734D(CP 0x11CALL NZ, 38:72DA— validates the range variable’s data layout via38:7260before accepting it) and38:7056(CP 0x11/CP 0x12distinguishing TblRng from the following type in the header switch).- The validation body at
38:72DAperforms generic parse-boundary checking. It calls38:7260, which reads the parse stream through the parser cursor block and accepts statement delimiters as valid terminations. The companion filter at38:72FFrejects token classes that cannot follow:0xB5,0xAB,0xEB,0xAA, and the0x41–0x64range except for a0x21second byte. Classification side effects land at0x8479and0x847A. TheTblRngspecial case therefore requires a legal statement boundary and reuses the validator called by the other parse stubs. [confirmed]