Skip to content
HN On Hacker News ↗

Bringing Swift to the Apple II

▲ 68 points 3 comments by LucidLynx 2w ago HN discussion ↗

Pangram verdict · v3.3

We believe that this document is primarily human-written, with some AI-generated and AI-assisted content detected

12 %

AI likelihood · overall

Mixed
90% human-written 8% AI-generated
SEGMENTS · HUMAN 6 of 6
SEGMENTS · AI 0 of 6
WORD COUNT 1,910
PEAK AI % 13% · §6
Analyzed
Jun 28
backend: pangram/v3.3
Segments scanned
6 windows
avg 318 words each
Distribution
90 / 8%
human / AI fraction
Verdict
Mixed
Pangram v3.3

Article text · 1,910 words · 6 segments analyzed

Human AI-generated
§1 Human · 0%

Swift is the modern programming language behind many apps that run on modern Apple platforms. I thought it would be nice to bring a small taste of it back to Apple’s early days, the Apple II. It was Apple’s first mass-market series of machines, initially released in 1977 with a 1 MHz MOS 6502 CPU. I built SwiftII, a Swift-flavored mini development environment for the original Apple II to a IIe and up. To be honest, I built it with heavy AI assistance in my spare time. I will be candid later how it helped because that workflow was as interesting to me as the retrocomputing work itself. One big caveat, this is not modern Swift and will never be. The full Swift standard library will not fit on this machine. SwiftII is a deliberate subset, much closer in spirit to Embedded Swift than the SDK meant for modern platforms like iOS or macOS. My goal was to fit as much of Swift into the Apple II as I can while still reading like Swift on sight. If you know Swift, you should be able to read a SwiftII-compatible program and immediately understand it. Other than the interpreter, I want my user to still have a relatively good user/developer experience. Because of this, I also had to build a launcher, file selector and text editor due to limitations I encountered on the Apple II. Contents This post is quite long, so here are the sections for you to jump straight to. The App Motivation Target Hardware The language SwiftII supported features Where the types differ from Swift The main constraint: 40,704 bytes Memory beyond 64K Development setup How the language works From source text to bytecode What the bytecode actually looks like No floating point at all Two families of binary Why quitting the REPL reboots the machine “What you type” vs “what you see” vs “what is stored” Typing Swift on a 1977 keyboard for II and II+ Text Display on

§2 Human · 0%

the Apple II Developing Swift on the machine itself The file browser The editor The trouble with memory banking Why this project ships as 9x disks Family A: REPL Family B: Compiler and Runner Testing too many machines How it was built with AI The ground rules The calls that were mine Claude and Codex did different jobs Why autonomous mode kept tripping here The documentation as the AI’s memory Try it Conclusion The App Boot the appropriate disk and you land in a launcher. You can select an interactive REPL, a file browser that runs .swift programs straight from the disk, a full-screen editor all self-contained on one bootable floppy. Here is a demo video of the app running on my original Apple II Plus. It goes through booting the system, running some sample programs and usage of the file selector, editor, REPL and compiler. The compiler is on a separate disk from the REPL. The original II Plus keyboard has no lowercase and no \ key, so the program is typed with digraphs. ??/ becomes \ which SwiftII interprets as canonical Swift. More on that quirk later. Here are some sample screenshots: If you are too excited to continue reading and want to dive into the code and disk images, here is the GitHub repo https://github.com/yeokm1/swiftii. Motivation I recently restored an Apple II Plus that was generously donated to me. I wondered what modern abilities I can make it do. Years ago, I wrote a DOS ChatGPT client and a Slack client for Windows 3.1. Those projects were about squeezing a modern network service into a vintage machine. This time I wanted to know if I can do the same for a modern programming language, one from Apple itself no less! The inspiration for this app is Apple Pascal. Back in 1979, Apple Pascal brought the UCSD p-System to the Apple II. Rather than compile Pascal to native 6502 machine code, it compiled to bytecode that ran on a virtual machine similar to how Java does it. I wanted to do the same for Swift. The compiler emits bytecode and a virtual machine (VM) executes it. Almost half a century apart, both languages reach the 6502 by avoiding native 6502 code generation through a VM.

§3 Human · 0%

I also wanted a Read–eval–print loop (REPL) tool so users can test their code in an interactive manner. The modern Swift SDK from Apple also supports this REPL function so I thought it’s reasonable to add that too. Target Hardware The baseline target is the original 1977 Apple II with appropriate hardware upgrades applied. If SwiftII runs here, it runs on pretty much any future Apple II. The 1979 Apple II Plus in the photo above is an incremental iteration over the original Apple II with features like Applesoft BASIC, more RAM and disk Autostart. Otherwise, it is largely similar to the original Apple II. My II Plus has the following high-level specifications: MOS 6502 CPU 1 MHz 48 KB main RAM Clone Saturn 128K RAM card (backward compatible as 16K Language Card) Clone Videx Videoterm 80-column card Yellowstone universal disk controller card (can emulate original Disk II mode) Floppy Emu to emulate Disk II drives More details on my own Apple II Plus setup are documented here: https://github.com/yeokm1/retro-configs-apple/tree/main/apple-ii-plus. The original 1977 Apple II is supported as long as it has been upgraded to 48 KB RAM plus a 16 KB language card giving the same 64 KB total. On a non-Autostart ROM machine you start the Disk II manually with C600G or PR#6. The Apple IIe is the nicer machine to use because it has lowercase support, a better display ROM and a proper ASCII keyboard. On a IIe, you can type SwiftII source much more naturally instead of relying on the Apple II Plus digraph and input conversion layer which I will share more later. I used ProDOS 2.4.3 because it is the modern still-maintained ProDOS 8 revival that is well documented and still runs on an original Apple II. Using a single ProDOS reference image also made the disk build and testing much simpler than supporting multiple DOS/ProDOS variants. A minimum 16K Language Card is mandatory for ProDOS. The 6502 is an 8-bit CPU.

§4 Human · 1%

Its A, X and Y registers are all 8 bits wide and there is no general-purpose 16-bit register. There is no floating-point unit and not even a hardware multiply or divide instruction. The hardware stack is only 256 bytes which limits function call depth. As for memory, 64 KB is tiny by today’s standards more akin to a microcontroller. The language Here is a taste of what SwiftII looks like. If you know Swift, you should find this very familiar. let answer = 42 var count = 0 let maybe: Int? = 5 if let x = maybe { print(x) } let n = maybe ?? 0 while count < 10 { count += 1 } for i in 0..<5 { print(i) } func greet(name: String) -> String { return "Hello, \(name)!" } var xs = [1, 2, 3] xs.append(4) print("\(xs.count) items") SwiftII supported features The core (lite disks) reads exactly like Swift: let and var with type inference if / else if / else, while and for-in over ranges Short-circuit && / || and prefix ! Top-level functions with return Optionals with if let, ?? and force-unwrap ! Arrays with append, count, isEmpty and subscripting Strings with + concatenation, \(...) interpolation and String(n) Beyond that, additional capabilities depend on which disk you boot: the extras (sat/aux) disks add integer parsing (Int(s)), byte/string helpers (asc, chr), more array methods (removeLast, removeAll, contains), peek/poke, cursor/text control and lo-res graphics (gr, color, plot, hlin, vlin); the Family B compiler disks go further with switch, for-in directly over arrays, random(in:), speaker tone and file I/O. So the same source can be a compile error on one disk and run fine on another depending on which features you try on which disk. Where the types differ from Swift Types behave differently from conventional Swift: Int is 16-bit and signed due to the cc65 limit. It ranges −32768 to 32767. (Conventional Swift is 64-bit on modern systems) It is also the only numeric type, no Double and no Float.

§5 Human · 0%

I will cover more on this in the section below. String is just bytes, not Unicode. Modern Swift treats a string as a collection of Character objects. SwiftII strings are ASCII byte sequences, closer to C-style string arrays. Items in arrays have to be all the same type. Identifiers are capped at 11 characters to save space. A longer name is a hard compile error rather than a silent truncation so two long names can never collide on a shared prefix. There are also no closures, no dictionaries, no error-handling or throws, no concurrency (async/await), no macros and no call-site argument labels. Each would cost memory or single-pass compiler complexity the machine obviously cannot spare. The main constraint: 40,704 bytes To understand the memory budget, you first have to understand the Apple II’s memory map. The 6502 can address 65536 bytes with its 16-bit address bus. SwiftII binaries runs under ProDOS as a SYS binary, which starts at $2000. Since they do not leave room for BASIC.SYSTEM above it, the practical load-image ceiling is the contiguous range from $2000 up to the ProDOS global page at $BF00 -> \$2000-$BEFF. I got AI to help me to draw up this infographic. That region is exactly 40,704 bytes. It is the entire main-RAM my binary can use. Memory beyond 64K 64KB is just enough to run core features of SwiftII but is not sufficient for more functions or bigger programs. However, memory beyond 64 KB is not directly addressable. Memory upgrades of that time period like Saturn 128K card or the IIe’s 64K of auxiliary RAM does not extend the address space like modern linear memory. It sits behind a window and software banks pieces of it in and out through soft switches. If you come from the DOS PC world, it is conceptually similar to Expanded Memory Specification (EMS). The extra RAM is there, but the CPU can only see the selected page at the selected address range. On the Apple II this is especially awkward because the language-card window is also where ROM, ProDOS ProDOS Machine Language Interface (MLI) code also lives.

§6 Human · 13%

Only one bank can be visible there at a time. I will come back to this topic later. Development setup The entire project is written largely in C90 and is compiled in two ways: clang on my Mac for the unit tests. cc65 for the Apple II where it actually ships. cc65 is a C cross-compiler for 6502 systems. Why C and not Assembly? Getting AI to write in Assembly or directly into machine code binary itself could probably squeeze out more efficiency gains. But a project filled with 6502 assembly would be much harder for me to inspect and change directly. C keeps the project at a level where, if the AI tools are wrong/unavailable or I have to debug the last mile myself, I can still understand and modify the code in a feasible way. I think the tradeoff is justified. How the language works A SwiftII program flows through a conventional-looking pipeline: Swift source ---> lexer ---> compiler ---> bytecode ---> VM ---> output (tokens) (Pratt) (.swb) Most language implementations parse source into an Abstract Syntax Tree (AST) then walk that tree to generate code.