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

14 — RAM pages

The TI-84 Plus has banked RAM pages behind the Z80’s 16 KiB windows. TI-OS normally keeps RAM page 81 in 8000-BFFF and RAM page 80 in C000-FFFF, but ROM helpers temporarily map other pages for paged memory access. On OS 2.55MP, traces show page 83 writes during boot/home initialization and during homescreen expression entry. Programs that borrow page 83 must preserve or restore the OS-visible regions below.

Page selectors [confirmed]

The 84+ memory ports use two encodings:

WindowPortSelector encodingNormal TI-OS value
4000-7FFF6bit 7 clear selects Flash page value & 0x3F; bit 7 set selects RAM page 0x80 | (value & 7)banked Flash page
8000-BFFF7bit 7 clear selects Flash page value & 0x3F; bit 7 set selects RAM page 0x80 | (value & 7)81
C000-FFFF5low three bits select RAM page 0x80 | (value & 7)00 → RAM page 80

This rule matches the dynamic resolver, TilEm’s x4 memory mapper, and the OS trace. In the idle boot/home trace, the RAM-window writes are:

OUT (port 7) <- 0x7f   8000-BFFF = page_3F
OUT (port 7) <- 0x81   8000-BFFF = RAM/0x81
OUT (port 5) <- 0x00   C000-FFFF = RAM/0x80
OUT (port 7) <- 0x80   8000-BFFF = RAM/0x80
OUT (port 7) <- 0x81   8000-BFFF = RAM/0x81
OUT (port 5) <- 0x02   C000-FFFF = RAM/0x82
OUT (port 7) <- 0x83   8000-BFFF = RAM/0x83
OUT (port 7) <- 0x81   8000-BFFF = RAM/0x81
OUT (port 5) <- 0x00   C000-FFFF = RAM/0x80

The final restore values are therefore port 7 = 0x81 and port 5 = 0x00 for normal OS execution.

Page map [standard, cross-checked]

WikiTI’s RAM pages page is a useful public map, but OS 2.55MP needs the page-83 warnings to be read literally. The local dump at wikiti-dump/main/83Plus:OS:Ram Pages.wiki carries the same current page-83 notes. The local trace and disassembly support this table:

RAM pageUse
80Normal C000-FFFF RAM page. The boot/home trace restores this with OUT (5),0. WikiTI marks it execution-protected.
81Normal 8000-BFFF RAM page. This contains the visible TI-OS RAM variables, OP registers, flags, graph buffers, user heap, and VAT window documented in tools/ram.txt and ti83plus.inc.
82Not a general OS work page under normal execution. The idle trace maps it briefly through port 5 as part of a paged RAM helper, then restores page 80. WikiTI marks it execution-protected.
83Shared OS scratch/state page. OS 2.55MP maps it through port 6 for block copies and LCD capture, and through port 7 for a paged byte-store helper. Homescreen expression entry writes the previous-entry buffer at 577E.
84Not used by TI-OS under typical execution; WikiTI marks it execution-protected.
85Not used by TI-OS under typical execution on full-RAM hardware.
86Not used by TI-OS under typical execution; WikiTI marks it execution-protected.
87Not used by TI-OS under typical execution on full-RAM hardware.

On newer 48 KiB hardware, WikiTI says RAM pages 82-87 alias the same physical memory. WikiTI’s port 15 page identifies ASIC value 55h as the 48 KiB TA1 ASIC. Programs that use page 83 must not treat 82-87 as independent storage on that hardware. [standard]

Per-page trace coverage [confirmed]

The boot/home and 2+3 ENTER traces exercise startup, homescreen initialization, display capture, parsing, evaluation, and previous-entry storage. They do not exercise app launch, USB transfer, graph drawing, archive cleanup, or a 48 KiB ASIC. Within that scope, physical RAM-page writes are:

RAM pageIdle trace writes2+3 ENTER trace writesInterpretation
80256227 writes, all page addresses touched345702 writes, all page addresses touchedNormal high RAM page selected by port 5; contains stack/system/user RAM activity in the C000-FFFF window.
8162947 writes, all page addresses touched72638 writes, all page addresses touchedNormal 8000-BFFF RAM page; contains the documented OS variables, flags, OP registers, heap, VAT window, and working buffers.
82no writes observedno writes observedPort 5 briefly selects raw value 02, but the observed store is through page 83 in bank B. No page-82 storage is confirmed by these traces.
831882 writes to 43D9-44BD and 5A7E-5DF23467 writes to 4373-4390, 43D9-44BD, 577E-5790, and 5A7E-5DF2Shared OS scratch/state page. See the range table below.
84no writes observedno writes observedNo typical-use OS storage confirmed in these traces.
85no writes observedno writes observedNo typical-use OS storage confirmed in these traces.
86no writes observedno writes observedNo typical-use OS storage confirmed in these traces.
87no writes observedno writes observedNo typical-use OS storage confirmed in these traces.

The zero-write rows mean “not hit by these scenarios,” not a global proof that the page is never used. On 48 KiB hardware, pages 82-87 alias page 83, so writes intended for 84-87 would not be independent storage even if a program can select those page numbers. [standard]

The graph scenario in tools/macros/graph-y1-x2.macro reaches the graph screen and still only writes pages 80, 81, and 83. It increases normal page-80/81 activity but leaves page-83 at the same confirmed ranges as the idle trace. It does not hit pages 82 or 84-87. [confirmed]

How to hit the confirmed paths

The useful distinction is between “page number can be selected” and “the OS uses it in a normal workflow.” These paths are confirmed or have a concrete next scenario:

Page/pathHow to hit itEvidence
80 high RAMRun any cold-boot, home, expression, or graph trace.Port 5 = 00 is the normal restore value; every current trace writes all page-80 addresses. [confirmed]
81 normal bank-B RAMRun any cold-boot, home, expression, or graph trace.Port 7 = 81 is the normal restore value; every current trace writes all page-81 addresses. [confirmed]
83 display captureRun boot-idle.macro or graph-y1-x2.macro.Ghidra shows _SaveDisp (39:5DD8) calls lcd_read_block (ram:1890) at the 39:5E03 call site; coverage hits both, and writes 5A7E-5D7D. [confirmed]
83 homescreen previous-entry historyRun home-2plus3.macro.The trace adds 577E-5790, advances lastEntryPTR from 577E to 5791, and sets numLastEntries to 01. [confirmed]
83 expression scratch copyRun home-2plus3.macro.The trace adds 4373-4390 through flash_copy_block at ram:1868/ram:187C. [confirmed]
83 split-screen/table copyEnter a split-screen/table workflow that calls _ScreenSplit.Ghidra shows _ScreenSplit at 05:7712 calls flash_copy_block at 05:772A; this path is not hit by the current macros. [confirmed]
83 edit-buffer initializationEnter an edit-buffer workflow that reaches editbuf_init_buf.Ghidra shows editbuf_init_buf at 03:6BC4 calls flash_copy_block at 03:6BCD; this path is not hit by the current macros. [confirmed]
83 app-menu state restoreOpen an app/menu workflow that reaches mnu_restore_app_state.Ghidra shows mnu_restore_app_state at 39:6D96 calls flash_copy_block at 39:6DA0; this path is not hit by the current macros. [confirmed]
84-87 independent pagesUse a forced RAM-page probe or a ROM path that passes pair index 2 or 3 to the computed bank-pair helper.The ROM can compute these selectors, but raw immediate selector scans and current traces do not show a normal OS path selecting or writing them. [hypothesis]

The computed bank-pair helpers use this selector formula:

    LD A,B
    SLA A
    OUT (5),A        ; pair index 0/1/2/3 -> pages 80/82/84/86 in bank C
    INC A
    OR 0x80
    OUT (7),A        ; pair index 0/1/2/3 -> pages 81/83/85/87 in bank B

Decoded callers set B = 1, selecting pages 82/83; that explains the observed port 5 = 02, port 7 = 83 sequence. Pages 84–87 are reachable through the helper but are not selected on any observed OS path [hypothesis]. The B = 1 caller pattern is confirmed for the decoded callers above. [confirmed]

Page 83 use [confirmed and standard]

Page 83 is the page people most often borrow as scratch, but the ROM uses it as more than anonymous free RAM. Keep the evidence classes separate:

RangeUseEvidence
4373-4390Expression-path page-83 scratch copyAdded by the 2+3 ENTER trace. The block write is the LDIR at ram:187E in the page-83 copy helper (page 83 mapped via OUT (6),A at ram:187C); the caller is still unlabeled. [confirmed]
43D9-44BDBoot/home page-83 scratch copyPresent in the idle trace. The block write is the LDIR at ram:187E in the page-83 copy helper (page 83 mapped via OUT (6),A at ram:187C), plus one byte stored at 37:44D8. [confirmed]
577E-5A7DHomescreen previous-entry historyPage 33 references 577E, the 5A7E upper bound, lastEntryPTR (0x8DA7), and numLastEntries (0x8E29). The 2+3 ENTER trace writes 577E-5790, advances lastEntryPTR to 5791, and sets numLastEntries to 01. [confirmed]
5A7E-5DF2LCD/home display capture areaPresent in the idle trace. The _SaveDisp LCD capture (ram:1890) fills the first 0x300 bytes, 5A7E-5D7D (the 96×64 framebuffer); the 5D7E-5DF2 tail is additional page-83 writes in the same scenario. Ghidra decompiles ram:1890 as an LCD-read helper that maps page 83 through port 6 and stores bytes read from LCD port 11. [confirmed]
4000-4080App base-page staging before app executionWikiTI public note; the two traces on this page do not launch an app. [standard, not traced here]
4100-433AUSB communication buffersWikiTI public note; the two traces on this page do not exercise USB transfer. [standard, not traced here]

Ghidra identifies the page-83 block-copy helper at ram:1868. It saves the current port-6 value, writes 0x83 to port 6, runs LDIR, and restores the previous page through the page-set helper:

ram:1877  IN A,(6)
ram:1879  PUSH AF
ram:187A  LD A,0x83
ram:187C  OUT (6),A
ram:187E  LDIR
ram:1880  POP AF
ram:1881  CALL 0x181C

Ghidra identifies the LCD capture helper at ram:1890. It maps page 83, waits on the LCD, reads port 11, and stores each byte through HL:

ram:189F  IN A,(6)
ram:18A1  PUSH AF
ram:18A2  LD A,0x83
ram:18A4  OUT (6),A
ram:18A6  CALL 0x0CC3
ram:18A9  IN A,(0x11)
ram:18AB  LD (HL),A

The reset path on page 37 initializes the previous-entry pointers:

37:6E0D  LD HL,0x577E
37:6E10  LD (lastEntryPTR),HL
37:6E13  LD HL,0x0000
37:6E16  LD (numLastEntries),HL

Page 38 has a second clear path with the same pointer reset:

38:422D  LD HL,0x577E
38:4230  LD (lastEntryPTR),HL
38:4233  LD HL,0x0000
38:4236  LD (numLastEntries),HL

The homescreen entry-history code on page 33 uses the same constants and variables:

33:53D1  LD A,(numLastEntries)
33:53E2  LD HL,0x5A7E
33:53F7  LD HL,0x577E
33:5430  LD A,(numLastEntries)
33:543A  LD DE,0x577E
33:5451  LD DE,0x577E
33:5459  LD (lastEntryPTR),HL
33:5462  LD HL,numLastEntries
33:5465  INC (HL)

If a program modifies the history buffer on page 83, clearing numLastEntries at 0x8E29 prevents the homescreen from scrolling back into invalid entry data. That is the public WikiTI recovery advice, and the ROM confirms that 0x8E29 is the OS-visible previous-entry count. [standard, address confirmed]

Dynamic test scenarios

The trace analyzer maps TilEm memory-write records back to physical RAM pages. Use it with full-range traces:

ROM=/path/to/ti84plus_2.55mp_complete.rom
tilem2 --headless --rom "$ROM" --model ti84p --normal-speed --reset \
  --macro tools/macros/boot-idle.macro \
  --trace /tmp/page83-idle.trace --trace-range all
tilem2 --headless --rom "$ROM" --model ti84p --normal-speed --reset \
  --macro tools/macros/home-2plus3.macro \
  --trace /tmp/page83-2plus3.trace --trace-range all
tilem2 --headless --rom "$ROM" --model ti84p --normal-speed --reset \
  --macro tools/macros/graph-y1-x2.macro \
  --trace /tmp/page83-graph.trace --trace-range all
python3 tools/analyze_ram_page_trace.py /tmp/page83-idle.trace --page 0x83
python3 tools/analyze_ram_page_trace.py /tmp/page83-2plus3.trace --page 0x83
python3 tools/analyze_ram_page_trace.py /tmp/page83-graph.trace --page 0x83

The baseline idle trace writes:

RAM page 0x83 writes: 1882
unique page addresses: 1114
range 43D9-44BD
range 5A7E-5DF2

The 2+3 ENTER trace writes:

RAM page 0x83 writes: 3467
unique page addresses: 1163
range 4373-4390
range 43D9-44BD
range 577E-5790
range 5A7E-5DF2

The before/after RAM variables line up with the previous-entry write:

ScenariolastEntryPTR (0x8DA7)numLastEntries (0x8E29)
Idle home screen577E00
After 2+3 ENTER579101

Those values come from end-of-trace RAM reconstruction. The added page-83 range 577E-5790 is exactly the bytes between the old and new lastEntryPTR values. [confirmed]

Restoring after page 83

Restore the selector for every window you changed. For code entered from normal TI-OS state that temporarily maps page 83 into bank B (8000-BFFF) and page 82 into bank C (C000-FFFF), restore the two RAM windows this way:

    LD A,0x81
    OUT (7),A        ; 8000-BFFF back to RAM page 81
    XOR A
    OUT (5),A        ; C000-FFFF back to RAM page 80

For code that maps page 83 into bank A (4000-7FFF), preserve and restore port 6:

    IN A,(6)
    PUSH AF

    LD A,0x83
    OUT (6),A        ; map RAM page 83 at 4000-7FFF
    ; use 4000-7FFF here

    POP AF
    OUT (6),A        ; restore previous Flash/RAM page selector

Keep the nonstandard mapping inside a short critical section. The OS helper preserves interrupt state around the temporary RAM-page mapping so the interrupt handler does not run with bank A or bank B pointing at page 83.

For code that may be called with nonstandard paging, preserve and restore the selectors for all touched windows:

    IN A,(6)
    PUSH AF
    IN A,(7)
    PUSH AF
    IN A,(5)
    PUSH AF

    LD A,0x83
    OUT (7),A        ; map RAM page 83 at 8000-BFFF
    ; use 8000-BFFF here

    POP AF
    OUT (5),A
    POP AF
    OUT (7),A
    POP AF
    OUT (6),A

The OS’s own paged byte-store helper at 37:44AE uses the normal restore pattern:

37:44D0  OUT (5),A        ; A = page index << 1, trace case A = 0x02 (→ RAM page 82)
37:44D2  INC A            ; A = 03
37:44D3  OR 0x80          ; A = 0x83
37:44D5  OUT (7),A        ; trace case: 0x83
37:44D7  LD A,B
37:44D8  LD (DE),A        ; byte store while RAM page 83 is visible
37:44D9  LD A,0x81
37:44DB  OUT (7),A
37:44DD  XOR A
37:44DE  OUT (5),A

The dynamic trace resolves the same sequence at instruction indices 712241-712250, including the final port 7 = 81 and port 5 = 00 writes. [confirmed]