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

Apps, memory reset & settings

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

How the student-facing parts of the OS work: launching Flash Apps, the MEM → Reset menu (what “RAM Cleared” erases), and the MODE screen format/mode flags. Addresses are space:addr where ram/page_00=0000-3FFF, flash pages mapped at 4000-7FFF. Confidence flags follow conventions.md: [confirmed] = read from disassembly; [hypothesis] = strong inference, not yet verified (used below for both strongly-inferred claims and partial traces where the code is RAM-resident / cross-page and not fully traced).

Cross-references: doc 11 (contexts, _AppInit, event router), doc 12 (RAM heap, _CleanAll), doc 13 (flash page map). Flag bits use the ti83plus.inc equates; the SystemFlags base is IY = flags = 0x89F0, so e.g. (IY+0x0A) = flags + fmtFlags.


1. Flash Apps — find & launch

This ROM ships with zero bundled apps in the local ROM-byte scan (zero 80 0F headers found at page starts) [hypothesis], but the entire find/launch machinery is present on page 0x3D (_FindApp*) and page 0x3B (_AppInit glue / app-quit). Apps are TI Flash Applications: a contiguous run of 16 KiB flash pages whose first page begins with a TLV app header.

1.1 App header format (TLV) [confirmed]

An app header is a sequence of type-length-value fields starting at offset 0 of the app’s first page. Each field begins with two bytes in WikiTI’s TT TS notation: the high 12 bits are the field number, and the low nibble of the second byte encodes the payload length. The decoder bytes are around 3D:7285, but the disassembly does not expose a live function there:

size nibblefield payload size
0xD1 byte
0xE2 bytes
0xF4 bytes
3D:7285  AND 0x0F; CP 0x0F -> B=4 ; CP 0x0E -> B=2 ; CP 0x0D -> B=1

The master field at offset 0 is usually 80 0F … (field 800, size nibble F, followed by a 4-byte size) — this is what the page-scan keys on to recognise an app. Fields carry the app name, the page count, flags, the date stamp, and signature-related data.

The public header descriptions match the ROM parser and the local app corpus. Useful references are WikiTI’s application-header and certificate/header format pages, TI’s AppHeader guide, and Tari’s Cemetech disassembly note, which describes .8xk data as Intel HEX pages based at 0x4000 and app code as starting after field 807.

Common app-header fields in the sample corpus:

fieldmeaningobserved payload
800master Flash-variable field800F with a four-byte app length at the start of every sampled app
801developer/signing key0104, the TI-83+/84+ freeware/shareware app key
802program revisionone-byte revision, usually 1
803build numberone-byte build number, usually 1; MirageOS uses 2
804app nameup to 8 bytes; examples include Axe, MirageOS, USBDRV8X, and zStart
808page countone byte; matches the decoded page count for Axe and CtlgHelp’s two-page apps
809disable TI splash screenusually zero-length when present; zStart uses a 15-byte app-owned payload
80Clowest basecodeusb8x uses 02 1E, decoded as basecode 2.30
032date stampsix bytes; bytes 1-4 decode as seconds since 1997-01-01
020date-stamp signature / unchecked payloadusually 64 bytes; Axe stores executable helper bytes here
807final fieldterminates the parsed header; the 807F length bytes are ignored

The app header is not a fixed 128-byte struct. The 807 final field terminates it. The common 80 7F 00 00 00 00 form uses size nibble F with a four-byte zero, but WikiTI documents that length as ignored; the shorter 80 70 form is valid. The app body begins after the final field and any app-controlled padding. Bytes before the conventional 4080 entry point are not loader magic; they are field payload or padding, and an app can choose payload bytes that also decode as Z80. [standard]

External sample check (not ROM evidence): the local Axe Parser Axe.8xk sample decodes to a base page whose 020D date-stamp-signature field starts at 4027 and has a 64-byte payload. Part of that payload is a Z80 helper at 4037:

ti-kid identified this Axe header case and published an annotated decode in Hatchet-Compiler; the local decode below uses that lead and verifies it against the extracted Axe.8xk bytes.

4037  POP AF
4038  POP BC
4039  POP DE
403A  POP HL
403B  PUSH HL
403C  PUSH DE
403D  PUSH BC
403E  PUSH AF
; ...
4056  LD A,0C9h
4058  CPIR
405A  PUSH HL
405B  IN A,(6)
405D  DEC A
405E  LD HL,4065h
4061  RST 20h
4062  JP 8478h
4065  OUT (6),A
4067  RET

RST 20h is _Mov9ToOP1, so the helper copies the thunk at 4065 into OP1 (0x8478) and jumps to OP1. That makes OUT (6),A; RET run from RAM after A has been set to the current bank-A page minus one. The preceding CPIR searches from HL for a RET byte (0xC9) and pushes the byte after it as the return address. The first half preserves the popped registers while it probes caller-owned bytes and can return early; the later page switch and RAM-thunk behavior are directly decoded from the sample bytes.

The same sample’s conventional entry area at 4080 starts NOP; JR 408C; JP 4097; JP 4548. tools/app_header_re.py reproduces this pass: --fetch-known downloads a local corpus from ticalc.org into ignored tools/app-samples/, and --markdown prints the decoded header table. The corpus keeps the same parser boundary rule:

app samplepages field / decoded pagesfinal field endentry bytes at 4080header-area note
Axe2 / 2407000 18 09 C3 97 40 C3 48020 payload contains the 4037 helper; then padding
MirageOS1 / 14070C3 D3 65 C3 D9 47 C3 D6padding to 4080
Omnicalc1 / 14070C3 8C 40 C3 E5 79 C3 70padding to 4080
CalcSys1 / 14070C3 89 40 21 AA 98 CB DEpadding to 4080
Symbolic1 / 1407018 2E 3A 4A 42 4A 4D 4Apadding to 4080
BatLib1 / 14070C3 25 61 C3 6E 43 C3 DEpadding to 4080
BatLib-modified Celtic 3 / Grammer / Omnicalc1 / 14070app-specific jump/vector bytessame boundary; nonzero 807F size bytes are ignored
zStart 1.3.013 / zStart831 / 1408018 11 83 C3 ...809D0F carries a 15-byte Z80 helper at 406B
CtlgHelp / zChem from zStart2 / 2 or 1 / 14070app-specific bytespadding to 4080
usb8x1 / 1402900 00 00 00 00 00 00 96mostly zero padding, plus JP 4180h; JP 42EAh at 4049

So 4080 is a common app-entry convention, not the OS’s header parser boundary. Some apps end the parsed header at 4029, 4070, or exactly 4080, and all remain valid because the 807 final field terminates the header.

The public entry points for walking these fields are bcalls in ti83plus.inc: _FindAppHeaderSubField (bcall 0x80AB) locates a field in an app header, and _FindOSHeaderSubField (bcall 0x8075) does the same for the OS header. Both build on the generic walkers _FindSubField (bcall 0x805D), _FindGroupedField (bcall 0x8030), and _GetFieldSize (bcall 0x805A), which decode the TLV length nibble shown above. These IDs sit in the boot-page bcall range (0x8000+); the 0x8040/0x8070/0x8080 helpers the OS also reaches are a distinct group in the same range and are not these field walkers. The body addresses behind these public entry points are not defined functions in the disassembly.

1.2 _FindApp / _FindAppUp / _FindAppDn [confirmed]

  • _FindApp (3D:5EE3) — locate an app by name (OP1). Inits the search page, then loops app_find_next_page (5FB1) + a header-match step until done, returning the app’s start page and a found/not-found flag via RST 28 (bcall) into RAM flash helpers.
    5EE3 CALL 727D            ; flash_set_sector_cnt -> appSearchPage (0x82A3)
    5EE6 CALL 5FB1            ; step to next candidate page (DEC appSearchPage)
    5EE9 RET C                ; ran off the end -> not found
    5EEA CALL 5EB2            ; read/compare this page's header
    5EED BIT 3,C; JR Z,5EE6   ; not a match -> keep scanning
    
  • app_find_next_page (3D:5FB1) — appSearchPage (0x82A3) -= 1; stops at page 7 (low boundary of the app region); bjumps appSearchPage:0x4000 to inspect the header.
  • flash_set_sector_cnt (3D:727D → helper 726E) — initializes 0x82A3 to the model-selected page base plus one.
  • _FindAppUp (5DDA) / _FindAppDn (5DE6) — enumerate the previous / next app in flash (for the APPS-menu list), both wrapping the common walker _app_5de7 (5DE7). _app_5de7 keeps two counts in BC (apps before/after) and tracks the current name in OP3.
  • _FindAppNumPages is present in the bcall table (3D:4AA3), but the disassembly has no function record at that address.

State variables: appSearchPage = 0x82A3, 0x8497/0x8481/0x9C87 are search-mode scratch (0x9C87=‘i’ selects the in-RAM “temp app” search variant).

1.3 Launching an app as a context [confirmed]

_AppInit (ram:0936, bcall 0x404B) installs a context from an app header:

_AppInit(byte *hdr):                 ; HL -> 13-byte vector block in the header
  copy 12 bytes hdr[0..11] -> cxMain (0x858D)   ; the 6 context vectors
  flags.appFlags (IY+0x0D) = hdr[12]            ; appFlags byte
  cxPage (0x8599) = port_mapBankA               ; the flash page the handlers run from

The 12 bytes are the 6 little-endian handler pointers (cxMain, cxPPutAway, cxPutAway, cxRedisp, cxErrorEP, cxSizeWind — see doc 11 §Context block). Example: the OS’s own default app vectors live at 3B:7571:

3E 75 | 4B 75 | 9F 74 | 4B 75 | 4B 75 | 4B 75 | 0A
cxMain=753E cxPPutAway=754B cxPutAway=749F cxRedisp=754B cxErrorEP=754B cxSizeWind=754B appFlags=0A

_ReloadAppEntryVecs (3B:73E4, bcall 0x4C36) calls _AppInit on that block, then overrides cxErrorEP (0x8595)=0x27D9. After _AppInit, the main event loop runs the app through call_context_main (pages in cxPage, jumps (cxMain)doc 11).

Because cxCurApp (0x859A) is a key code, pressing a mode key selects the context to load (doc 11). The App quit restore-path candidate at 3B:7412 is not a defined function in the disassembly; the saved-context restore behavior stands as a byte-trace note (the label is project-local, not a WikiTI or ti83plus.inc equate).


2. RAM clearing / memory reset

The MEM menu ([2nd][+], “MEMORY MANAGEMENT/DELETE” + “RESET”) and its messages are on page 0x01 (text/homescreen page). The reset engine is on page 0x35; the user-RAM re-init lands in page-0 boot code.

2.1 The user-facing strings (page 0x01) [confirmed]

AddrString
01:4076Defragmenting...
01:4098Arc Vars Cleared
01:40A9 Apps Cleared
01:40B8Arc Vars & Apps Cleared
01:4109Resetting All...
01:4126+412EGarbage + Collecting...
01:4234Resetting...
01:7425..746Emenu titles: RESET MEMORY, RESET DEFAULTS, RESET ARC VARS, RESET ARC APPS, RESET ARC BOTH, RESET RAM
01:747Ethe long “Resetting ALL / RAM / Vars / Apps / Both …” warning help text

2.2 The reset dispatcher (mem_reset_dispatch @ 35:7180) [confirmed]

Dispatch is on the selected reset item held in keyExtend (0x8446):

keyExtendactionmessage shown
1reset archived varsArc Vars Cleared (path 720B)
2reset archived appsApps Cleared (path 7267)
3reset both arc vars+appsArc Vars & Apps Cleared (path 7275)
4reset all (RAM+archive)Resetting All... (path 71F0)
else (0)RAM reset (“RAM Cleared”)wipe + re-init (path 719F)

2.3 What “RAM Cleared” (RAM reset) zeroes [confirmed]

The RAM-reset path (35:719F):

719F BIT 1,(IY+0x35); JP Z,0x0B2F          ; first-stage vs full path select
71A6 LD HL,(0x9B73)                         ; preserve a saved word
71B4 LD A,(IY+0x3F); AND 0x7F               ; keep low 7 bits (clear bit 7) of flag byte 0x3F
71B9 DI
71BA LD HL,0x8000; LD DE,0x8001; LD BC,0x1BC3; LD (HL),0; LDIR   ; *** zero system RAM 0x8000-0x9BC3 ***
71C7 LD (IY+0x3F),A                         ; restore the saved low 7 bits
...   (restore IY+0x34 bit6, IY+0x35 bit0 from the preserved state)
71E0 LD HL,0x9BD0; LD DE,0x9BD1; LD BC,0x642F; LD (HL),0; LDIR   ; *** zero user RAM 0x9BD0-0xFFFF ***
71ED JP 0x0BD9                              ; re-init RAM (page-0 boot init)

So a RAM reset clears two blocks to 0:

  1. System RAM 0x8000–0x9BC3 (~7 KiB: OS scratch, the Context block, system buffers).
  2. User RAM 0x9BD0–0xFFFF (0x6430 = 25648 bytes, ~25 KiB: the VAT and all user variables/programs).

A handful of flag bits are explicitly preserved across the wipe (IY+0x3F bit7, IY+0x34 bit6, IY+0x35 bits0/1, and the word at 0x9B73) so the calculator knows it is mid-reset. It then JP 0x0BD9, the RAM-init entry (OUT (0) page select, LD SP,0xFFF7, then CALL 0x3EC1 — the cross-page trampoline that rebuilds the VAT, system vars, and LCD; see doc 11), which rebuilds a clean default VAT and system state and re-enters the homescreen. The Flash archive is not touched by a plain RAM reset.

2.4 Full reset (page_0/ram:0B27) [confirmed]

The harder reset (RESET ALL / power-on cold start) is at ram:0B27:

0B27 LD SP,0; ... 0B37 DI; OUT (0),0xC0
0B41 LD HL,0x8000; LD DE,0x8001; LD BC,0x7FFF; LD (HL),0; LDIR   ; zero ALL of 0x8000-0xFFFF (32 KiB)
0B4E ... preserve/inspect IY+0x3F; select sub-path; JP 0x3EA9/0x3EAF

This zeroes the entire 32 KiB RAM and does the deepest re-init.

2.5 _CleanAll / cleanup_temp_ram (07:52CF) — not a reset [confirmed]

Distinct from the MEM reset. _CleanAll (bcall 0x4A50) only compacts temporary RAM after a command finishes: it shifts the FP stack (fpBase/FPS) down to tempMem, resets the OPBase/OPS/pTemp scratch pointers, and clears pTempCnt/cleanTmp. It does not clear the VAT, user vars, or Flash (see doc 12). _FixTempCnt (07:4FEC) marks temps ≥ a count reclaimable then tail-calls the same compaction.

2.6 Flash archive GC — “Defragmenting…” / “Garbage Collecting…” [confirmed behavior; display-label addresses undisassembled]

Separate from RAM reset: when the Flash archive fills, the OS rewrites live archived vars to fresh sectors and erases the old ones. The display dispatcher sits around 3C:7E23 (shows Defragmenting... 0x4076) / 7E10/7E1C (shows Garbage Collecting... 0x4126+412E); 3C:7E00 is not a defined function in the disassembly (the label is project-local, not a WikiTI or ti83plus.inc equate). It clears 0x844B (curRow, the text-row cursor — reset before the banner draws) and runs with the screen frozen (DI). The actual sector erase/write primitives are RAM-resident (flash control port 0x14) — see doc 12.


3. MODE / settings flags

The flag bytes live in the SystemFlags area at IY = 0x89F0. The MODE screen (cxMode = kMode = 0x45) is a menu context that flips these bits; the canonical setters below show exactly which bits.

3.1 Angle: Degree vs Radian — trigFlags (IY+0) [confirmed]

trigDeg = bit 2 of trigFlags (0x89F0): 1 = Degrees, 0 = Radians. (Confirmed against WikiTI Flags:00 and the ROM — _Sin (02:7342) tests BIT 2,(IY+0) to pick the degree path.)

SET 2,(IY+0)   ; FD CB 00 D6  -> Degree
RES 2,(IY+0)   ; FD CB 00 96  -> Radian
BIT 2,(IY+0)   ; FD CB 00 56  -> tested by _Sin/_Cos/_Tan to select degree vs radian

Math routines branch on this bit to choose degree/radian variants (_SinCosRad etc. force radians; the degree paths convert first).

3.2 Graph type: Func / Param / Polar / Seq — grfModeFlags (IY+0x02) [confirmed]

The four graph-mode setters on page 0x36 are mutually exclusive: each first clears all four bits via clr_grfmode (36:7D00), then ORs in its own bit, then calls _SetTblGraphDraw. param_1 is IY, so *(param_1+2) = grfModeFlags.

clr_grfmode (36:7D00):  grfModeFlags &= 0xEF & 0xDF & 0xBF & 0x7F   ; clear bits 4,5,6,7
bcalladdrbit setflag (inc)
_SetFuncM36:7D11bit 4 (|0x10)grfFuncM (Function)
_SetPolM36:7D2Cbit 5 (|0x20)grfPolarM (Polar)
_SetParM36:7D39bit 6 (|0x40)grfParamM (Parametric)
_SetSeqM36:7D1Fbit 7 (|0x80)grfRecurM (Sequence/Recursion)

Each setter first calls a small predicate (36:0013/0254/0259/025E) and only re-sets the mode if the parity/condition flag (F bit6) requires it, avoiding needless redraws.

Other grfModeFlags bits (from inc, not in the setters above): bit3 grfPolar (rect↔polar coordinate readout). Related graph bytes: grfDBFlags (IY+0x04) bit0 grfDot (line/dot), bit1 grfSimul (sequential/simultaneous), bit4 grfNoCoord, bit5 grfNoAxis; seqFlags (IY+0x0F).

3.3 Numeric format: Normal/Sci/Eng, Float/Fix, base — fmtFlags (IY+0x0A) [confirmed (bits from inc)]

fmtFlags byte at 0x89FA:

bitnamemeaning
0fmtExponent1 = show exponent (Sci/Eng), 0 = Normal
1fmtEng1 = Engineering, 0 = Scientific (when exponent on)
2-4fmtBaseMask (fmtHex/fmtOct/fmtBin)integer base (Dec/Hex/Oct/Bin)
5fmtRealreal display mode
6fmtRectrectangular complex display (a+bi)
7fmtPolarpolar complex display (re^θi)

So Normal/Sci/Eng = (bit0, bit1): Normal = 00, Sci = 01, Eng = 11. fmtOverride (IY+0x0B, 0x89FB) is a working copy used during conversions.

Float vs Fix N is not in fmtFlags — it is the separate byte fmtDigits = 0x97B0: value 0x00–0x09 = Fix-N decimal places, 0xFF = Float.

3.4 MODE screen plumbing

The MODE screen is a menu context (cxMode/kMode=0x45) reached via the event/key router (doc 11). Its row strings live as token names on page 0x01 (RadianN/DegreeO/NormalP/ Float at 01:49E4..4A06; trailing letters are token-id bytes) and full-caps menu labels on page 0x37 (DEGREE 4A85, RADIAN 4A8C). Selecting a row writes the flag bits documented above directly (SET/RES (IY+…), or stores into fmtDigits). [hypothesis] (partial) — the per-row write table itself is reached through the menu dispatcher and was not traced line-by-line, but every target bit/byte is confirmed from the setters and inc equates.


4. Confident space:addr index

3D:5EE3   _FindApp
3D:5DDA   _FindAppUp
3D:5DE6   _FindAppDn
3D:5DE7   _app_5de7
3D:5FB1   app_find_next_page
3D:727D   flash_set_sector_cnt
3D:7285   TLV-length candidate (inferred label); no defined function in live DB
3D:4AA3   _FindAppNumPages bcall target; no live function in current DB
ram:0936       _AppInit
ram:08AF       _PutAway
3B:73E4   _ReloadAppEntryVecs
3B:7571   default app vectors data block (12 bytes + appFlags), not a function
3B:7412   app-quit restore candidate (inferred label); no defined function in live DB
35:7180   mem_reset_dispatch
35:719F   ram_reset_wipe         (zeroes 0x8000-0x9BC3 and 0x9BD0-0xFFFF)
ram:0BD9       ram_init_after_reset
ram:0B27       full_reset_wipe        (zeroes all 0x8000-0xFFFF)
3C:7E00   archive-GC-display candidate (inferred label); no defined function in live DB
07:52CF   _CleanAll (cleanup_temp_ram)
07:4FEC   _FixTempCnt
36:7D11   _SetFuncM     (grfModeFlags bit4)
36:7D1F   _SetSeqM      (grfModeFlags bit7)
36:7D2C   _SetPolM      (grfModeFlags bit5)
36:7D39   _SetParM      (grfModeFlags bit6)
36:7D00   clr_grfmode   (clears grfModeFlags bits 4-7)

Key SystemFlags / RAM addresses

0x89F0  flags (IY base)
 +0x00  trigFlags   (bit2 trigDeg: 1=Degree,0=Radian)
 +0x02  grfModeFlags(bit4 Func,bit5 Polar,bit6 Param,bit7 Seq; bit3 grfPolar)
 +0x04  grfDBFlags  (bit0 Dot, bit1 Simul, bit4 NoCoord, bit5 NoAxis)
 +0x0A  fmtFlags    (bit0 Exponent, bit1 Eng, bit2-4 base, bit5 Real, bit6 Rect, bit7 Polar)
 +0x0B  fmtOverride
 +0x0D  appFlags
0x97B0  fmtDigits   (0-9 = Fix N, 0xFF = Float)
0x82A3  appSearchPage
0x8446  keyExtend   (reset-submenu selector 1..4; extended-key state)
0x858D  cxMain ...  0x8599 cxPage  0x859A cxCurApp   (Context block, doc 11)