A cross-platform emulator for TI-84 Plus CE calculators, with native Android, iOS, and Web apps.
calc/
core/ # Rust emulator core (C ABI + WASM)
android/ # Android app (Kotlin + Jetpack Compose)
ios/ # iOS app (Swift + SwiftUI)
web/ # Web app (React + TypeScript + Vite)
scripts/ # Build scripts
docs/ # Architecture and milestone documentation
cemu-ref/ # CEmu reference emulator (git-ignored, optional)
The mobile apps (Android & iOS) and web app are designed to work with two interchangeable emulator backends:
┌─────────────────────────────────────────────────────────┐
│ App (UI) │
│ Android (Kotlin/Compose) │
│ iOS (Swift/SwiftUI) │
│ Web (React/TypeScript) │
├─────────────────────────────────────────────────────────┤
│ C API (emu.h) / WASM Bindings │
│ emu_create, emu_load_rom, emu_run_cycles, │
│ emu_framebuffer, emu_set_key, ... │
├───────────────────────┬─────────────────────────────────┤
│ Rust Core │ CEmu Adapter │
│ (libemu_core.a) │ (libcemu_adapter.a) │
│ (emu_core.wasm) │ (cemu.wasm via Emscripten) │
│ │ │
│ Our implementation │ Wraps CEmu reference │
│ from scratch │ emulator │
└───────────────────────┴─────────────────────────────────┘
Both backends implement the same C API (core/include/emu.h), allowing the apps to switch between them at build time without any code changes.
The Rust core (core/) is our from-scratch implementation of the TI-84 Plus CE hardware:
- eZ80 CPU - Full instruction set with ADL mode (24-bit addressing)
- Memory - Flash (4MB), RAM (256KB), VRAM, memory-mapped I/O
- Peripherals - LCD controller, keypad, timers, RTC, interrupts, SPI
- Scheduler - Cycle-accurate event scheduling
CEmu is the established open-source TI-84 Plus CE emulator. We use it as a reference implementation for:
- Parity testing - Compare our emulation against known-correct behavior
- Debugging - When something doesn't work, check if CEmu behaves the same
- Development - Test app features before Rust implementation is complete
The CEmu adapter (android/app/src/main/cpp/cemu/) wraps CEmu's C code to match our API.
The emulator recreates the TI-84 Plus CE hardware in software:
- Loads ROM - The calculator's operating system (you provide this)
- Executes CPU - Runs eZ80 instructions at ~48MHz emulated speed (this can be changed to run at higher or lower speeds)
- Renders Display - 320x240 16-bit color LCD at 60 FPS
- Handles Input - 8x7 key matrix matching the physical calculator
- Emulates Peripherals - Timers, real-time clock, interrupts, etc.
The apps provide the UI (screen display, keypad, menus) while the backend handles all emulation logic.
- Rust toolchain
- For Android: Android Studio with NDK, Android SDK (API 24+)
- For iOS: Xcode 15+, macOS
- For Web: Node.js 18+, wasm-pack
The project uses a single build script for both platforms:
./scripts/build.sh <platform> [options]Platforms: android, ios
Options:
| Option | Description |
|---|---|
--release |
Release build (default) |
--debug |
Debug build |
--cemu |
Use CEmu backend instead of Rust |
--install |
Android: Install APK after build |
--sim |
iOS: Build for Simulator |
--all-abis |
Android: Build all ABIs (default: arm64 only) |
Note: For iOS, the build script only compiles the backend library. Open
ios/Calc.xcodeprojin Xcode to build and run the app.
Examples:
# Android
./scripts/build.sh android # Release, arm64, Rust
./scripts/build.sh android --debug --install # Debug + install to device
./scripts/build.sh android --cemu # Release with CEmu backend
# iOS (builds backend library, then open Xcode to build app)
./scripts/build.sh ios # Release, device, Rust
./scripts/build.sh ios --sim --debug # Simulator, Debug
./scripts/build.sh ios --sim --cemu # Simulator, CEmu backend# Android
make android # Release, arm64, Rust
make android-debug # Debug, arm64, Rust
make android-cemu # Release, CEmu backend
make android-install # Release + install
make android-cemu-install # CEmu + install
# iOS (builds backend library, then open Xcode to build app)
make ios # Release, device, Rust
make ios-debug # Debug, device, Rust
make ios-cemu # Release, device, CEmu
make ios-sim # Release, simulator, Rust
make ios-sim-cemu # Release, simulator, CEmu
# Web
make web # Build WASM + web app (production)
make web-dev # Start dev server with hot reload
make web-cemu # Build with CEmu WASM backend
# Utilities
make test # Run Rust tests
make clean # Clean all build artifacts
make help # Show all targetsOne-time setup - Install Android targets:
rustup target add aarch64-linux-android # ARM64 (most devices)
rustup target add armv7-linux-androideabi # ARM32 (older devices)
rustup target add x86_64-linux-android # x86_64 emulator
rustup target add i686-linux-android # x86 emulatorManual Gradle build (if not using build.sh):
cd android
./gradlew assembleDebugOne-time setup - Install iOS targets:
rustup target add aarch64-apple-ios # iOS device (arm64)
rustup target add aarch64-apple-ios-sim # iOS Simulator (Apple Silicon)
rustup target add x86_64-apple-ios # iOS Simulator (Intel)Building and Running:
# 1. Build the backend library
./scripts/build.sh ios --sim # For simulator
./scripts/build.sh ios # For device
# 2. Open Xcode and build/run the app
open ios/Calc.xcodeproj
# Press Cmd+R to build and runThe build script compiles the emulator backend (Rust or CEmu) as a static library. Xcode handles building the Swift app and linking against the library.
One-time setup - Install WASM target and wasm-pack:
rustup target add wasm32-unknown-unknown
cargo install wasm-packBuilding and Running:
# Development (with hot reload)
make web-dev
# Production build
make web
# Output in web/dist/The web app runs entirely in the browser using WebAssembly (~96KB gzipped).
Keyboard Controls:
| Key | Function |
|---|---|
| 0-9 | Number keys |
| + - * / | Math operations |
| ( ) | Parentheses |
| ^ | Power |
| . | Decimal |
| , | Comma |
| _ | Negate (-) |
| Enter | Enter |
| Backspace / Delete | Del |
| Arrow keys | Navigation |
| Escape | Clear |
| Shift | 2nd |
| Alt | Alpha |
| O | ON |
| Space | Pause emulation |
| V | √ (square root) |
| S / C / T | Sin / Cos / Tan |
| L / G | Ln / Log |
| M | Math |
| R | x⁻¹ |
| X | X,T,θ,n |
| Insert | Sto |
| F1-F5 | Y= / Window / Zoom / Trace / Graph |
| Home | Apps |
| P | Prgm |
| PageDown | Prgm |
| PageUp | Vars |
| End | Stat |
| Ctrl+R / Cmd+R | Resend last program file |
For rapid iteration on Android:
# Terminal 1: Watch for Kotlin changes and auto-deploy
cd android
./watch.sh
# When changing Rust code, rebuild:
./scripts/build.sh android --debug --installThe watch.sh script requires fswatch (brew install fswatch).
The apps can be built with CEmu instead of our Rust core. This is useful for:
| Use Case | Description |
|---|---|
| Parity Testing | Compare behavior: does our Rust core produce the same results as CEmu? |
| Bug Investigation | If something breaks, check if it's our bug or a ROM/hardware quirk |
| Feature Development | Test new app features (UI, input) before Rust implementation catches up |
| Performance Baseline | Compare frame rates and responsiveness |
Setup:
# Clone CEmu (one-time, git-ignored)
git clone https://github.com/CE-Programming/CEmu.git cemu-refBuild with CEmu:
./scripts/build.sh android --cemu # Android with CEmu backend
./scripts/build.sh ios --sim --cemu # iOS simulator with CEmu backend
./scripts/build.sh ios --cemu # iOS device with CEmu backend
# Then open ios/Calc.xcodeproj in Xcode to build the app
make web-cemu # Web with CEmu WASM backendThe app behavior should be identical regardless of backend - if it differs, that's a bug to investigate.
cd core
cargo testThis runs the test suite covering:
- Memory subsystem (Flash, RAM, VRAM, ports)
- Bus address decoding and wait states
- eZ80 CPU instructions and flag behavior
- ADL mode 24-bit operations
- TI-84 CE memory map verification
To run a specific test:
cargo test test_nameTo see test output:
cargo test -- --nocaptureTest tools in tools/cemu-test/ compare CEmu (reference emulator) behavior with our Rust implementation.
Prerequisites:
- Clone CEmu:
git clone https://github.com/CE-Programming/CEmu.git cemu-ref - Build CEmu core:
cd cemu-ref/core && make - Obtain a TI-84 Plus CE ROM file
Build the tools:
cd tools/cemu-test
makeparity_check - Verifies RTC timing, MathPrint flag, and key state at cycle milestones:
./parity_check # Defaults: 60M cycles, ROM at ../../TI-84 CE.rom
./parity_check /path/to/rom.rom -m 100000000 # Custom ROM and cycle count
./parity_check -v # Verbose modeKey addresses monitored:
0xD000C4- MathPrint flag (bit 5: 1=MathPrint, 0=Classic)0xF80020- RTC control register (bit 6: load in progress)0xF80040- RTC load status (0x00=complete, 0xF8=all pending)
trace_gen - Generates CPU instruction traces for direct comparison:
./trace_gen ../../TI-84\ CE.rom # 1M steps to stdout
./trace_gen ../../TI-84\ CE.rom -n 100000 -o cemu_trace.txt # To fileOutput format: step cycles PC SP AF BC DE HL IX IY ADL IFF1 IFF2 IM HALT opcode
The emulator includes a consolidated debug tool for testing, tracing, and diagnostics.
Run these from the core/ directory:
cd core
cargo boot # Run boot test with progress reporting
cargo screen # Render screen to PNG after boot
cargo vram # Analyze VRAM colors
cargo trace # Generate trace log (100k steps)
cargo dbg # Show debug tool help
cargo t # Run all tests
cargo rb # Release buildFor more options, use the debug tool directly (from core/):
cargo run --release --example debug -- <command>| Command | Description |
|---|---|
boot |
Run boot test with progress reporting |
trace [steps] |
Generate trace log for parity comparison (default: 100k) |
screen [output] |
Render screen to PNG after boot (default: screen.png) |
vram |
Analyze VRAM content (color histogram) |
compare <file> |
Compare our trace with CEmu trace file |
sendfile <files> |
Inject .8xp/.8xv files into flash, boot, and screenshot |
bakerom <out> [files] |
Create a new ROM with programs pre-installed in flash |
help |
Show help message |
Examples:
# Generate 1M step trace for parity comparison
cargo run --release --example debug -- trace 1000000
# Save screenshot with custom name
cargo run --release --example debug -- screen boot.png
# Compare with CEmu trace
cargo run --release --example debug -- compare ../traces/cemu.logFor detailed parity testing with CEmu:
# 1. Generate CEmu trace
cd tools/cemu-test
./trace_gen ../../TI-84\ CE.rom -n 10000 -o cemu_trace.txt
# 2. Generate Rust trace
cd ../../core
cargo run --release --example debug -- trace 10000 > rust_trace.txt
# 3. Compare
diff ../tools/cemu-test/cemu_trace.txt rust_trace.txt | head -50
# Or use the built-in compare command
cargo run --release --example debug -- compare ../tools/cemu-test/cemu_trace.txt- BYOR (Bring your own ROM) - Obtain a TI-84 Plus CE ROM file legally
- Install the app
- Use "Import ROM" to load your ROM file
- Press Run to start emulation
The emulator supports loading TI-84 CE programs (.8xp) and AppVars (.8xv) into the calculator's flash archive. This is how you run third-party programs like games.
From the app: Use the file picker in the web, Android, or iOS app to select .8xp/.8xv files. They will be injected into flash and available in the program menu after the next boot.
From the CLI: The debug tool provides two commands for loading programs:
Loads the ROM, injects files into flash, boots the emulator, and saves a screenshot. Useful for quick testing.
cd core
# Inject a single program
cargo run --release --example debug -- sendfile DOOM.8xp
# Inject a program with its required C libraries
cargo run --release --example debug -- sendfile DOOM.8xp clibs/*.8xvPrograms that use the CE C toolchain (e.g., games built with graphx) need their library .8xv files included. Common libraries: libload.8xv, graphx.8xv, keypadc.8xv, fileioc.8xv.
Creates a new ROM file with programs pre-installed in the flash archive. The output ROM can be loaded directly and programs will appear in TI-OS without needing to inject files each time.
cd core
# Bake a ROM with DOOM pre-installed
cargo run --release --example debug -- bakerom doom.rom DOOM.8xp clibs/*.8xv
# Then use the baked ROM like any other ROM file
cargo run --release --example debug -- boot # (with doom.rom as your ROM)This is useful for creating a ROM that's ready to go with your favorite programs already installed.
See LICENSE file.
This emulator does not include any copyrighted ROM or OS images. You must provide your own legally obtained ROM file.
