Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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]

AddrNameMeaningToken
0x92B3TblMin (a.k.a. TblStart)first independent value in the tabletTblMin/TBLMINt = 0x1A
0x92BCTblStep (ΔTbl)increment between successive rowstTblStep/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:

BitNameMeaning
4 (0x10)autoFillIndpnt: 0 = Auto (fill X from TblStart/ΔTbl), 1 = Ask (prompt for each X)
5 (0x20)autoCalcDepend: 0 = Auto (compute Y immediately), 1 = Ask (compute a cell only on request)
6 (0x40)reTable0 = 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:

VarTokenVarToken
Y10x10Y60x15
Y20x11Y70x16
Y30x12Y80x17
Y40x13Y90x18
Y50x14Y00x19

(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:

bcallAddrRole
_PUT_INDEX_LST33:7066store a value in slot n at 0x84D9 + 2n
_GET_INDEX_LST33:707Aload the value in slot n through _LdHLind
_HEAP_SORT33:7097sort 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:

  1. 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 _StoX for each row.
  2. Evaluate each selected equation against the current X through bcall ID 4741h. Its body at 35:7C7C drives the page 38 parser cluster: parse_init (38:5B7B), fps_alloc_to_9652 (38:5B10), 38:5ADA, and the _ParseInp region at 38:5987. The result remains in OP1.
  3. 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):

AddrRole
0x918C XOutSym / 0x918E XOutDatX column: symbol + data pointer
0x9190 YOutSym / 0x9192 YOutDatactive Y column: symbol + data pointer
0x9194 inputSym / 0x9196 inputDatthe “Ask”/input column descriptor
0x9198 prevDataprevious-column data pointer
0x91DBunnamed Ask-mode row state
0x91DC / 0x91DDCurTableRow / CurTableCol
0x91E0table-top state; exact role remains open
0x91E2table_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:6DFF calls the Indpnt test at 05:6D4C and invokes the entry-line editor at 05:7303. The editor pushes continuation 05:7329 onto the OPS stack through ram:27DA and enters setup at 05:5F64 and 05:5F51. On success, 05:6032 shifts the table_value_cache band and enters row evaluation at 05:615C. [confirmed]
  • Depend = Auto (bit5=0): Y cells compute immediately during the fill.
  • Depend = Ask (bit5=1): the gate at 05:6DD1 tests bit 5 through 05:6D67 and 05:6D56. In Ask mode, 05:69D2 checks cell state at 0x91CE and 0x8D1B, then calls 05:637C for one deferred evaluation. That routine pushes continuation 05:644E onto the OPS stack through ram:27DA and 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:

SiteTrigger
02:7B35 bytesediting TblStart/ΔTbl/Indpnt/Depend in TBLSET
37:5F3Dtoggling Indpnt or Depend on the setup screen
38:6340, 38:4809, 38:54CDthe 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:64DERES 6,(IY+0x13)). [confirmed]


End-to-end example: tabulating Y1=X² + 1

  1. Y=: types X²+1 after Y1=. The editor tokenizes it and stores the bytes as the EquObj Y1 (token 5E 10) in the VAT, with its flags byte’s select bit set (the = is highlighted). The parser store path sets reTable.
  2. TBLSET (2nd WINDOW): sets TblStart=0 (TblMin 0x92B3), ΔTbl=1 (TblStep 0x92BC), Indpnt:Auto, Depend:Auto. Each edit sets reTable (the setup bytes around 02:7B35).
  3. TABLE (2nd GRAPH): enters context cxTableEditor (0x4A) on page 05. table_editor_main (05:5D0D) sees reTable=1table_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 ID 4741h35:7C7C and the page 38 parser cluster (_Find_Parse_Formula / _ParseInp) → OP1 = X²+1, format and stash into table_value_cache.band[0]/band[1],
    • advance to the next row (bound-checked at 05:65DC; X = TblStart + k·TblStep) and repeat,
    • clear reTable.
  4. The grid paints (05:7E45) the cached X and Y1 columns as large-font text; scrolling (05:6014) slides the cache and computes only newly exposed rows.
  5. Deselecting Y1 (or editing the formula, or changing ΔTbl) sets reTable again 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 tblFlags bit layout, and which sites set/clear reTable: [confirmed] (equates + byte-verified bit-ops).
  • Page 05 = TABLE subsystem, the recompute→clear-reTable structure, the running-X seed from TblMin and +TblStep advance, 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 ID 4741h35:7C7C equation dispatcher → page-38 parser cluster) and the once-per-row _ParseInp execution are [confirmed] by a headless TilEm trace of Y1=X². _StoX does not execute during the fill; the running X moves through OP registers and FPS slots. _PUT_INDEX_LST, _GET_INDEX_LST, and _HEAP_SORT are generic indexed-list helpers; their bodies do not prove that TABLE uses iMathPtr4 for its selected equations.
  • Y= selection bit (0x20) — flags byte 0x23 selected / 0x03 deselected — and the style byte 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 continuation 05:7329. Depend=Ask evaluates individual cells at 05:637C, with OPS continuation 05:644E. See Auto and Ask modes.
  • _Find_Parse_Formula’s TblRng (type 0x11) special-case is [confirmed] at two byte sites: 38:734D (CP 0x11
    CALL NZ, 38:72DA — validates the range variable’s data layout via 38:7260 before accepting it) and 38:7056 (CP 0x11 / CP 0x12 distinguishing TblRng from the following type in the header switch).
  • The validation body at 38:72DA performs generic parse-boundary checking. It calls 38:7260, which reads the parse stream through the parser cursor block and accepts statement delimiters as valid terminations. The companion filter at 38:72FF rejects token classes that cannot follow: 0xB5, 0xAB, 0xEB, 0xAA, and the 0x410x64 range except for a 0x21 second byte. Classification side effects land at 0x8479 and 0x847A. The TblRng special case therefore requires a legal statement boundary and reuses the validator called by the other parse stubs. [confirmed]