A full port of the BasiliskII Macintosh 68k emulator to the ESP32-P4 microcontroller, running on the M5Stack Tab5 hardware. This project brings classic Mac OS (System 7.x through Mac OS 8.1) to a portable embedded device with touchscreen input and USB peripheral support.


This project runs a Motorola 68040 emulator that can boot real Macintosh ROMs and run genuine classic Mac OS software. The emulation includes:
- CPU: Motorola 68040 emulation with FPU (68881)
- RAM: Configurable from 4MB to 16MB (allocated from ESP32-P4’s 32MB PSRAM)
- Display: 640×360 virtual display (2× scaled to 1280×720 physical display)
- Storage: Hard disk and CD-ROM images loaded from SD card
- Input: Capacitive touchscreen (as mouse) + USB keyboard/mouse support
The Tab5 features a unique dual-chip architecture that makes it ideal for this project:
| Chip | Role | Key Features |
|---|---|---|
| ESP32-P4 | Main Application Processor | 400MHz dual-core RISC-V, 32MB PSRAM, MIPI-DSI display |
| ESP32-C6 | Wireless Co-processor | WiFi 6, Bluetooth LE 5.0 (not used by emulator) |
| Component | Details |
|---|---|
| Display | 5″ IPS TFT, 1280×720 (720p), MIPI-DSI interface |
| Touch | Capacitive multi-touch (ST7123 controller) |
| Memory | 32MB PSRAM for emulated Mac RAM + frame buffers |
| Storage | microSD card slot for ROM, disk images, and settings |
| USB | Type-A host port for keyboard/mouse, Type-C for programming |
| Battery | NP-F550 Li-ion (2000mAh) for portable operation |
See boardConfig.md for detailed pin mappings and hardware documentation.
The emulator leverages the ESP32-P4’s dual-core RISC-V architecture for optimal performance:
┌─────────────────────────────────────────────────────────────────┐
│ ESP32-P4 (400MHz) │
├────────────────────────────┬────────────────────────────────────┤
│ CORE 0 │ CORE 1 │
│ (Video & I/O Core) │ (CPU Emulation Core) │
├────────────────────────────┼────────────────────────────────────┤
│ • Video rendering task │ • 68040 CPU interpreter │
│ • 8-bit to RGB565 convert │ • Memory access emulation │
│ • 2×2 pixel scaling │ • Interrupt handling │
│ • Touch input processing │ • ROM patching │
│ • USB HID polling │ • Disk I/O │
│ • ~15 FPS refresh rate │ • 40,000 instruction quantum │
└────────────────────────────┴────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────┐
│ 32MB PSRAM Allocation │
├──────────────────────────────────────────────────────────────┤
│ Mac RAM (4-16MB) │ Configurable via Boot GUI │
├────────────────────────────┼─────────────────────────────────┤
│ Mac ROM (~1MB) │ Q650.ROM or compatible │
├────────────────────────────┼─────────────────────────────────┤
│ Mac Frame Buffer (230KB) │ 640×360 @ 8-bit indexed color │
├────────────────────────────┼─────────────────────────────────┤
│ Display Buffer (1.8MB) │ 1280×720 @ RGB565 │
├────────────────────────────┼─────────────────────────────────┤
│ Free PSRAM │ Varies based on RAM selection │
└──────────────────────────────────────────────────────────────┘
The video system uses an optimized pipeline for converting the Mac’s 8-bit indexed framebuffer to the display:
- 68040 CPU writes to emulated Mac framebuffer (640×360, 8-bit indexed)
- Video Task (Core 0) reads framebuffer at ~15 FPS
- Palette Lookup converts 8-bit indices to RGB565
- 2×2 Scaling doubles pixels horizontally and vertically
- DMA Transfer pushes 1280×720 RGB565 buffer to MIPI-DSI display
This port includes the following BasiliskII subsystems, adapted for ESP32:
| Component | File(s) | Description |
|---|---|---|
| UAE CPU | uae_cpu/*.cpp |
Motorola 68040 interpreter |
| ADB | adb.cpp |
Apple Desktop Bus for keyboard/mouse |
| Video | video_esp32.cpp |
Display driver with 2× scaling |
| Disk | disk.cpp, sys_esp32.cpp |
HDD image support via SD card |
| CD-ROM | cdrom.cpp |
ISO image mounting |
| XPRAM | xpram_esp32.cpp |
Non-volatile parameter RAM |
| Timer | timer_esp32.cpp |
60Hz/1Hz tick generation |
| ROM Patches | rom_patches.cpp |
Compatibility patches for ROMs |
| Input | input_esp32.cpp |
Touch + USB HID handling |
The emulator works best with Macintosh Quadra series ROMs:
| ROM File | Machine | Recommended |
|---|---|---|
Q650.ROM |
Quadra 650 | ✅ Best compatibility |
Q700.ROM |
Quadra 700 | ✅ Good |
Q800.ROM |
Quadra 800 | ✅ Good |
68030-IIci.ROM |
Mac IIci |
Supported Operating Systems
| OS Version | Status | Notes |
|---|---|---|
| System 7.1 | ✅ Works | Lightweight, fast boot |
| System 7.5.x | ✅ Works | Good compatibility |
| Mac OS 8.0 | ✅ Works | Full-featured |
| Mac OS 8.1 | ✅ Works | Latest supported |
- Hardware: M5Stack Tab5
- Software: PlatformIO (CLI or IDE extension)
- SD Card: FAT32 formatted microSD card (8GB+ recommended)
Quick Start (Recommended)
Download a ready-to-use SD card image with Mac OS pre-installed:
📥 Download sdCard.zip
- Format your microSD card as FAT32
- Extract the ZIP contents to the root of the SD card
- Insert into Tab5 and boot
Alternatively, create your own setup with these files in the SD card root:
/
├── Q650.ROM # Macintosh Quadra ROM (required)
├── Macintosh.dsk # Hard disk image (required)
├── System753.iso # Mac OS installer CD (optional)
└── DiskTools1.img # Boot floppy for installation (optional)
To create a blank disk image:
# Create a 500MB blank disk image
dd if=/dev/zero of=Macintosh.dsk bs=1M count=500
Then format it during Mac OS installation.
Option 1: Pre-built Firmware (Easiest)
Download the latest release from GitHub:
📥 Download Latest Release
Flash using esptool.py:
# Install esptool if you don't have it pip install esptool # Flash all three binary files (connect Tab5 via USB-C) esptool.py --chip esp32p4 --port /dev/ttyACM0 --baud 921600 write_flash 0x0 bootloader.bin 0x10000 firmware.bin 0x8000 partitions.bin
Note: Replace /dev/ttyACM0 with your actual port:
- macOS:
/dev/cu.usbmodem*or/dev/tty.usbmodem* - Windows:
COM3(or similar) - Linux:
/dev/ttyACM0or/dev/ttyUSB0
Option 2: Build from Source
# Clone the repository git clone https://github.com/amcchord/M5Tab-Macintosh.git cd M5Tab-Macintosh # Build the firmware pio run # Upload to device (connect via USB-C) pio run --target upload # Monitor serial output pio device monitor
On startup, a classic Mac-style boot configuration screen appears:
┌─────────────────────────────────────────┐
│ BasiliskII │
│ Starting in 3... │
│ │
│ Disk: Macintosh.dsk │
│ RAM: 8 MB │
│ │
│ ┌─────────────────────────────────┐ │
│ │ Change Settings │ │
│ └─────────────────────────────────┘ │
└─────────────────────────────────────────┘
- 3-second countdown to auto-boot with saved settings
- Tap to configure disk images, CD-ROMs, and RAM size
- Settings persistence saved to
/basilisk_settings.txton SD card - Touch-friendly large buttons designed for the 5″ touchscreen
| Setting | Options | Default |
|---|---|---|
| Hard Disk | Any .dsk or .img file on SD root |
First found |
| CD-ROM | Any .iso file on SD root, or None |
None |
| RAM Size | 4 MB, 8 MB, 12 MB, 16 MB | 8 MB |
The capacitive touchscreen acts as a single-button mouse:
- Tap=Click
- Drag=Click and drag
- Coordinates are scaled from 1280×720 display to 640×360 Mac screen
Connect a USB keyboard to the USB Type-A port. Supported features:
- Full QWERTY layout with proper Mac key mapping
- Modifier keys: Command (⌘), Option (⌥), Control, Shift
- Function keys F1-F15
- Arrow keys and navigation cluster
- Numeric keypad
- Caps Lock LED sync with Mac OS
Connect a USB mouse for relative movement input:
- Left, right, and middle button support
- Relative movement mode (vs. absolute for touch)
M5Tab-Macintosh/
├── src/
│ ├── main.cpp # Application entry point
│ └── basilisk/ # BasiliskII emulator core
│ ├── main_esp32.cpp # Emulator initialization & main loop
│ ├── video_esp32.cpp # Display driver (2× scaling, RGB565)
│ ├── input_esp32.cpp # Touch + USB HID input handling
│ ├── boot_gui.cpp # Pre-boot configuration GUI
│ ├── sys_esp32.cpp # SD card disk I/O
│ ├── timer_esp32.cpp # 60Hz/1Hz interrupt generation
│ ├── xpram_esp32.cpp # NVRAM persistence to SD
│ ├── prefs_esp32.cpp # Preferences loading
│ ├── uae_cpu/ # Motorola 68040 CPU emulator
│ │ ├── newcpu.cpp # Main CPU interpreter loop
│ │ ├── memory.cpp # Memory banking & access
│ │ └── generated/ # CPU instruction tables
│ └── include/ # Header files
├── platformio.ini # PlatformIO build configuration
├── partitions.csv # ESP32 flash partition table
├── boardConfig.md # Hardware documentation
└── scripts/ # Build helper scripts
| Metric | Value |
|---|---|
| CPU Quantum | 40,000 instructions per tick |
| Video Refresh | ~15 FPS |
| Boot Time | ~15 seconds to Mac OS desktop |
| Responsiveness | Usable for productivity apps |
- Dual-core separation: CPU emulation and video rendering run independently
- Large instruction quantum: Fewer context switches=faster emulation
- Direct framebuffer access: No intermediate copies
- Optimized 4-pixel batch processing: Reduced loop overhead in scaling
- Polling-based interrupts: Safer than async timers for stability
Key build flags in platformio.ini:
build_flags= -O2 # Optimize for speed -DEMULATED_68K=1 # Use 68k interpreter -DREAL_ADDRESSING=0 # Use memory banking -DROM_IS_WRITE_PROTECTED=1 # Protect ROM from writes -DFPU_IEEE=1 # IEEE FPU emulation
| Problem | Solution |
|---|---|
| “SD card initialization failed” | Ensure SD card is FAT32, properly seated |
| “Q650.ROM not found” | Place ROM file in SD card root directory |
| Black screen after boot | Check serial output for errors; verify ROM compatibility |
| Touch not responding | Wait for boot GUI to complete initialization |
| USB keyboard not working | Connect to Type-A port (not Type-C) |
| Slow/choppy display | Normal; emulator runs at ~15 FPS |
Connect via USB-C and use:
Look for initialization messages:
========================================
BasiliskII ESP32 - Macintosh Emulator
Dual-Core Optimized Edition
========================================
[MAIN] Free heap: 473732 bytes
[MAIN] Free PSRAM: 31676812 bytes
[MAIN] Total PSRAM: 33554432 bytes
[MAIN] CPU Frequency: 360 MHz
[MAIN] Running on Core: 1
[PREFS] Loading preferences...
[PREFS] RAM: 16 MB
[PREFS] Disk: /Macintosh8.dsk (read-write)
[PREFS] CD-ROM: None
[PREFS] Preferences loaded
[SYS] SD card should already be initialized by main.cpp
[MAIN] Allocating 16777216 bytes for Mac RAM...
[MAIN] Mac RAM allocated at 0x481ca674 (16777216 bytes)
[MAIN] Loading ROM from: /Q650.ROM
[MAIN] ROM file size: 1048576 bytes
- BasiliskII by Christian Bauer and contributors — the original open-source 68k Mac emulator
- UAE (Unix Amiga Emulator) — the CPU emulation core
- M5Stack — for the excellent Tab5 hardware and M5Unified/M5GFX libraries
- EspUsbHost — USB HID support for ESP32
- Claude Opus 4.5 (Anthropic) — AI pair programmer that made this port possible
This project is based on BasiliskII, which is licensed under the GNU General Public