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 & Y= variables

TI-84 Plus OS 2.55MP — feature deep dive.

What a college student touches when they enter functions in Y=, configure TBLSET (2nd WINDOW), and read the TABLE (2nd GRAPH) to tabulate Y1(X), Y2(X), … over a range of X. Traces: where the table-setup settings live → how the OS, per X row, sets the X variable, evaluates each selected Y= function through the parser into OP1, formats the result, and lays the values out as a text grid → how Y= equations are stored, selected, and styled → and the Table ↔ Y= ↔ parser ↔ display interaction.

Builds on sub-graphing.md (Y= storage, the regraph/eval path, plotSScreen), sub-tibasic.md (the page-38 parser, _Find_Parse_Formula, _ParseInp), 05-variables-vat.md (EquObj, _FindSym), 08-display-lcd.md (text grid via _PutMap/_PutC), and 11-boot-contexts-errors.md (the context/cxMain mechanism that selects the TABLE editor vs TABLE-setup screens).

Address form is page:addr (flash-page hex : logical offset; flash routines run mapped at 0x4000). RAM addresses are absolute. Confidence: [confirmed] = decompiled/byte-verified here, or multiple consistent signals (flag/token compares, call shape) pin it even where the dense Z80 handler bodies don’t fully reduce in the decompiler; [standard] = documented TI-83+/84+ behavior consistent with what was seen, not byte-pinned here; [hypothesis] = inferred, not yet verified.


0. The three pieces and how they connect

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 · _StoX sets the X system var"]
      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 exact same 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 for structure]


1. Table setup — where the settings live & how they’re read

System variables (RAM TIFloats) [confirmed addresses, ti83plus.inc + code]

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 token+addr]

Mode flags — tblFlags (IY+19 = IY+0x13) [confirmed bit layout]

From ti83plus.inc and verified by the bit-ops below:

BitNameMeaning
4 (0x10)autoFillIndpnt: 0 = Auto (fill X from TblStart/ΔTbl), 1 = Ask (prompt the student 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/edit handler — tblsetup_handler (02:7B20) [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/validation context — tblset_cx_display (37:5F10) [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]


2. Y= equation variables — storage, selection, 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 tokens]

Selection & 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 against the TI link-protocol var guide]

The selected-equation list — iMathPtr4 (0x84D9) [confirmed]

The OS keeps an in-RAM table of 2-byte pointers to the selected equations’ VAT entries, based at iMathPtr4 = 0x84D9. Three bcalls index it (verified by decompile — they compute 0x84D9 + 2·n):

bcallAddrRole
_GraphTblFind33:7097(re)build the list of selected-equation pointers
_GraphTblNext33:707A_LdHLind(0x84D9 + 2·n) — fetch the n-th equation pointer
_grf_706633:7066store a pointer into slot n (0x84D9 + 2·n)

This is the shared iterator the regraph driver and the table builder both walk to visit each selected Yn. [confirmed]

Resolving & evaluating a Y-var — _Find_Parse_Formula (38:758A) [confirmed]

_Find_Parse_Formula (bcall id 0x4AF2) is the universal “find a named var and parse/evaluate its stored formula” entry (see sub-tibasic.md §3). 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]


3. Table generation — the page-05 subsystem

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  (see §3.1)
         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 bytes]

3.1 Seeding the running independent value [confirmed]

05:774B initialises the per-table running X. It first clears a working float (05:774B LD DE,0x8622 ; CALL 0x1920), then at 05:7751 copies TblMin into the running-X slot:

05:7751  LD HL,0x92B3 (TblMin) ; LD DE,0x862B ; JP 0x1A92   ; running-X (0x862B) ← TblMin

i.e. the first row’s independent value is TblStart. A working float at 0x8622/0x862B holds the current row’s X. The row index is bounds-checked at 05:65DC (LD A,(0x91E0); LD HL,0x91DC; CP (HL); RET — current row vs the last row), and 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 student types each X; see §3.4.) [confirmed structure]

3.2 The per-row evaluation: set X, evaluate each selected Y [confirmed]

For each row the recompute fills the cache by, per selected equation:

  1. store the running-X into the X system variable (_StoX, 38:62A3),
  2. evaluate that equation’s tokens against the current X — the table walks the selected list via _GraphTblNext (33:707A) and runs each formula through the page-38 evaluator (_ParseInp 38:5987 / the _Find_Parse_Formula path), leaving Y in OP1 (exactly the grapher’s per-column eval in sub-graphing.md §6),
  3. format OP1 and stash the result string/value into the row’s cache slot.

The fill loop is 05:5EE1: it strides the cell buffer 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]

3.3 The value cache & scrolling [confirmed buffers]

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
0x91DB / 0x91DC / 0x91E0row indices / top-row index / column count
0x91E2 … 0x9221 …the per-cell computed-value buffers (9-byte floats)

05:6014 performs the scroll: LD HL,0x9221 ; LD DE,0x9260 ; LDIR (copy a 0x3F-byte block) and an LDDR shift of a 0xB4-byte region — i.e. when you press ↑/↓ past the cached window it slides the cache and only computes the one new row (or recomputes if reTable). [confirmed bytes]

3.4 Indpnt = Auto vs Ask, Depend = Auto vs Ask [confirmed test sites]

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 (§3.1).
  • Indpnt = Ask (bit4=1): the X column starts empty; the editor prompts the student to type each X, parses it (entry-line editor → _ParseInp), stores it to X, then evaluates the Y columns for only that row.
  • Depend = Auto (bit5=0): Y cells compute immediately during the fill.
  • Depend = Ask (bit5=1): Y cells show blank until the cursor lands on one and [ENTER] requests it, at which point that single cell is evaluated. [confirmed]

3.5 Rendering the grid to the LCD — 05:7E45 (column/row paint loop) [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, 08-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 text buffer (0x9221 vs 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 loop + buffers]

3.6 Split graph-table mode — _ScreenSplit (05:7712) [confirmed]

The G-T mode (graph on the left half, table on the right) is set up by _ScreenSplit (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. So G-T mode shares the very same table cache + running-X driver, rendered into the right columns alongside the plot. [confirmed]


4. What marks the table dirty (reTable = 1) [confirmed sites]

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]


5. End-to-end: a student tabulates 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),
    • _GraphTblFind/_GraphTblNext (33:7097/707A) walk the selected equation list at iMathPtr4 (0x84D9) — here only Y1,
    • per row: _StoX the running-X, evaluate Y1’s tokens via the page-38 evaluator (_Find_Parse_Formula / _ParseInp) → OP1 = X²+1, format and stash into the cell cache (0x91E2/0x9221),
    • 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.

6. Confident addresses (space:addr → name)

; --- 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            ; per-row value-cache fill (0x91E2)
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 cell text buffer 0x9221/0x91E2
05:7712  _ScreenSplit                     ; 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  91DB/91DC/91E0  row idx / row idx / top-row & col count
RAM  91E2 / 9221     per-cell computed-value buffers (9-byte floats)
RAM  8622 / 862B     running independent value (current row's X)

; --- 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                          ; base of selected-equation pointer list
33:7097  _GraphTblFind                    ; (re)build selected-equation list
33:707a  _GraphTblNext                    ; fetch n-th equation ptr (0x84D9+2n)
33:7066  _grf_7066                        ; store n-th equation ptr
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 (per row)
38:67ae  _RclX  / 38:67a4 _RclY / 38:626c _StoY
33:5023  _GraphParseTok                   ; pre-scan equation tokens (graphable?)

; --- 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)

7. Confidence summary / 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 exact per-row _StoX + selected-Y eval calls inside 05:5EE1/the fill loop are [hypothesis] — the loop, buffers, and the shared selected-equation iterator (iMathPtr4 / _GraphTblNext) are confirmed; the individual on-page direct CALLs to _StoX/the evaluator were inferred from the identical grapher per-column path rather than each byte-traced.
  • 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 (entry-line editor → _ParseInp_StoX for a typed X, single-cell Depend:Ask compute) is [hypothesis]: the mode bit tests (05:6D40) are confirmed; the interactive prompt body overlaps the page-02 Input/entry handlers and was not fully reduced.
  • _Find_Parse_Formula’s TblRng (type 0x11) special-case is [confirmed] in the header switch but its full body (cross-page) was not traced here.