Dual Backend Architecture
The TI-84 Plus CE emulator supports two interchangeable backends: a custom Rust implementation and a CEmu adapter. Both backends implement the same C API, allowing the apps to switch between them at build time without code changes.Overview
Why Two Backends?
Rust Backend (Default)
Advantages:- From-scratch implementation: Full control over behavior and optimization
- Modern codebase: Memory-safe Rust with clean architecture
- Cross-platform: Single codebase for Android, iOS, and Web
- Performance: WASM optimizations, lazy allocation, batched processing
- Small bundle: ~96KB gzipped for web builds
- Production builds for all platforms
- Custom features and optimizations
- Educational analysis of emulator internals
CEmu Backend (Reference)
Advantages:- Proven accuracy: Established emulator used by CE community
- Reference implementation: Ground truth for behavior verification
- Battle-tested: Years of real-world usage and bug fixes
- Parity testing: Compare Rust behavior against known-correct CEmu
- Bug investigation: Determine if issues are emulation bugs or ROM quirks
- Feature validation: Test UI features before Rust implementation is complete
- Performance baseline: Compare frame rates and responsiveness
Building with Different Backends
Android
iOS
Web
Backend Differences
Flash Timing Modes
The Rust backend supports two flash timing modes:Parallel Flash (Default)
- Older TI-84 CE models (pre-2019)
- Constant timing: 10 cycles per flash read (configurable via port 0xE10005)
- Better compatibility: Works with all ROM versions
- More predictable: No cache behavior to account for
Serial Flash
- Newer TI-84 CE models (2019+)
- Cache-based timing: 2-3 cycles (hit), 197 cycles (miss)
- Higher performance: Faster when code is cache-hot
- More complex: 2-way set-associative cache simulation
Unmapped Region Timing
Unmapped memory regions (0x400000-0xCFFFFF, 0xD65800-0xDFFFFF):| Backend | Flash Mode | Cycles | Behavior |
|---|---|---|---|
| Rust | Parallel | 258 | Pseudo-random data |
| Rust | Serial | 2 | Pseudo-random data |
| CEmu | Serial | 2 | Pseudo-random data |
Port Timing
Memory-mapped I/O ports (0xE00000-0xFFFFFF): Both backends use identical port timing per CEmu’sport.c:
- Add 4 cycles before write
- Process the write (may trigger CPU speed change)
- Rewind by (4 - actual_port_cycles)
API Compatibility
Both backends implement the exact same C API fromcore/include/emu.h:
Parity Testing
The dual backend architecture enables rigorous testing:Trace Comparison
Generate execution traces from both backends and compare:Full Trace Comparison (JSON)
For detailed I/O-level comparison:- Register state before/after
- Memory reads/writes
- Port access
- Cycle deltas per operation
Parity Check Tool
Theparity_check tool runs both backends in parallel:
0xD000C4: MathPrint flag0xF80020: RTC control register0xF80040: RTC load status
Performance Comparison
| Metric | Rust | CEmu | Notes |
|---|---|---|---|
| WASM size | ~96KB | ~800KB | Gzipped bundle |
| Native FPS | 1000+ | 1000+ | Both exceed 60 FPS target |
| WASM FPS | 60+ | 30-40 | Rust optimized for web |
| Boot time | ~62M cycles | ~62M cycles | Identical behavior |
| Memory usage | ~5MB | ~8MB | Lazy allocation |
Backend Selection Guidelines
Use Rust Backend When:
- Shipping production builds (all platforms)
- Optimizing performance or bundle size
- Implementing new features
- Learning emulator internals
Use CEmu Backend When:
- Investigating emulation accuracy bugs
- Validating new peripheral implementations
- Testing app features before Rust core is complete
- Establishing baseline behavior for a ROM edge case
Implementation Details
Rust Backend Structure
CEmu Adapter Structure
Next Steps
- Emulator Core - Rust core architecture overview
- Memory Map - TI-84 CE address space
- CPU - eZ80 instruction set and timing