Great news! You already have 90% of CP/M working! Here's what you have:
✅ BDOS (Basic Disk Operating System) - Fully implemented in C
- File operations (open, close, read, write, search, delete, rename)
- Console I/O
- Disk management
- All in
8080.cviacpm_bdos_call()
✅ Disk System - Complete emulation
- 2 disk drives (A: and B:)
- 256KB each (77 tracks × 26 sectors × 128 bytes)
- Read/write sector support
- Sample files already created (WELCOME.TXT, HELP.TXT, README.TXT)
✅ Console I/O - Working
- Keyboard input with buffer
- Screen output
- All integrated with your CPMTerminalViewController
Added I/O ports 0xF0-0xFA for BIOS communication:
0xF0- Console status0xF1- Console input0xF2- Console output0xF3- Disk select0xF4- Set track0xF5- Set sector0xF6/F7- DMA address (lo/hi)0xF8- Disk read0xF9- Disk write0xFA- Disk home
Three new .asm files in your project root:
cpm_bios.asm - Full BIOS implementation (1.5KB at 0xFA00)
- 17 BIOS functions with standard CP/M jump table
- Interfaces with your C code via I/O ports
- Includes disk parameter headers and buffers
cpm_ccp.asm - Console Command Processor (2KB at 0xDC00)
- Command-line interface with prompt ("A>")
- Built-in commands: DIR, TYPE, ERA, REN
- Can load and run .COM files
cpm_boot.asm - Simple Bootstrap (starts at 0x0100)
- Sets up CP/M system vectors
- Minimal command loop for testing
- Good for initial testing
The FASTEST way to test is to use your existing system:
You already have DIR working via your existing command processor! Just run it through the CPMTerminalViewController.
The CPMTerminalViewController already connects to your CP/M BDOS. Try running your existing test programs.
To run authentic CP/M 2.2:
Download CP/M 2.2 disk images from:
- https://archive.org/details/compupro-cpm-2.2n
- https://github.com/brouhaha/cpm22 (source code to assemble)
Use cpmtools or extract from disk image:
# If you have cpmtools:
cpmcp -f compupro disk.img 0:CCP.COM ./ccp.com
cpmcp -f compupro disk.img 0:BDOS.COM ./bdos.comAdd to your Swift code:
// Load CCP at 0xDC00
let ccpHex = convertFileToHex("ccp.com")
codeload(ccpHex, 0xDC00)
// Load BDOS at 0xE400
let bdosHex = convertFileToHex("bdos.com")
codeload(bdosHex, 0xE400)
// Load BIOS at 0xFA00 (assemble cpm_bios.asm)
let biosHex = assembleFile("cpm_bios.asm")
codeload(biosHex, 0xFA00)
// Set up vectors
mem[0x0000] = 0xC3 // JMP opcode
mem[0x0001] = 0x00 // Low byte of warm boot (0xFA00)
mem[0x0002] = 0xFA // High byte
mem[0x0005] = 0xC3 // JMP opcode
mem[0x0006] = 0x06 // Low byte of BDOS entry (0xE406)
mem[0x0007] = 0xE4 // High byte
// Start at BIOS cold boot
cpu_set_pc(0xFA00)The simplest test:
Use your existing Assemble.swift to assemble cpm_boot.asm
let bootHex = assembleFile("cpm_boot.asm")
codereset()
codeload(bootHex, 0x0100)
cpu_set_pc(0x0100)
// Start emulator
startEmulator(withProgram: bootHex, org: 0x0100)This will give you a basic "A>" prompt where you can type DIR to see files!
- Purpose: Hardware abstraction layer
- Location: 0xFA00-0xFFFF (1.5KB)
- Functions: Console I/O, disk I/O, system initialization
- How it works: Uses I/O ports to call your C functions
- Purpose: Command-line interface
- Location: 0xDC00-0xE3FF (2KB)
- Commands: DIR, TYPE, ERA, REN, plus load .COM files
- How it works: Reads user input, parses commands, calls BDOS
- Purpose: Minimal test program
- Location: 0x0100+ (wherever you load it)
- What it does: Sets up CP/M vectors, provides simple command loop
- Best for: Quick testing without needing real CP/M binaries
- Build and run the app
- Open CPMTerminalViewController
- Type commands and see output
- Test DIR command (should show WELCOME.TXT, HELP.TXT, README.TXT)
- Test TYPE WELCOME.TXT (should display file contents)
- Create a new file
- Verify file persistence
- Check that
cpm_consoleis initialized - Verify
cpm_get_char()is being called in the timer loop - Check that output buffer isn't full
- Ensure BDOS function 10 (Read Console Buffer) is being called
- Check that command parsing is working
- Verify the command buffer is at the right address
- Run
cpm_init()to create sample files - Check disk initialization in
cpm_disk_init() - Verify directory entries are being written
Once basic CP/M is working:
- Add more BDOS functions - Random access, file attributes, etc.
- Implement .COM file loading - Run actual CP/M programs
- Disk persistence - Save/load disk images to iOS files
- More commands - STAT, PIP, ED, ASM
- Load real CP/M software - WordStar, dBase, games!
┌─────────────────────────────────────┐
│ User Interface (Swift) │
│ ┌─────────────────────────────┐ │
│ │ CPMTerminalViewController │ │
│ │ - Keyboard input │ │
│ │ - Screen output │ │
│ │ - cpm_put_char() │ │
│ │ - cpm_get_char() │ │
│ └─────────────────────────────┘ │
└─────────────────────────────────────┘
↕
┌─────────────────────────────────────┐
│ 8080 Emulator (C) │
│ │
│ ┌───────────────────────────────┐ │
│ │ CP/M BIOS (0xFA00) │ │
│ │ - Console I/O (ports) │ │
│ │ - Disk I/O (ports) │ │
│ └───────────────────────────────┘ │
│ │
│ ┌───────────────────────────────┐ │
│ │ CP/M CCP (0xDC00) │ │
│ │ - Command parser │ │
│ │ - DIR, TYPE, ERA, REN │ │
│ └───────────────────────────────┘ │
│ │
│ ┌───────────────────────────────┐ │
│ │ CP/M BDOS (C code) │ │
│ │ - File operations │ │
│ │ - Console I/O │ │
│ │ - Disk management │ │
│ │ - Via cpm_bdos_call() │ │
│ └───────────────────────────────┘ │
│ │
│ ┌───────────────────────────────┐ │
│ │ Disk Emulation │ │
│ │ - disk_a[256KB] │ │
│ │ - disk_b[256KB] │ │
│ │ - 77 tracks × 26 sectors │ │
│ └───────────────────────────────┘ │
└─────────────────────────────────────┘
You've done the hard work - the disk system, file operations, and BDOS are all working. Now you just need to:
- Assemble one of the .asm files
- Load it into memory
- Run it
And you'll have a working CP/M system on iOS! 🎉