Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
/intermediates
/build
/shoebill
.DS_Store
*.xcworkspace
xcuserdata
/gui/build
/debugger/debugger
/debugger/debugger.dSYM
/sdl-gui/shoebill
/sdl-gui/shoebill.dSYM
61 changes: 61 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Changelog

## [0.2.0] - 2026-03-21

### Core Emulation Overhaul
- **Event-driven timer scheduler**: VIA timer checks are now inline with the CPU loop (every 512 steps), eliminating the dedicated VIA clock thread and `via_cpu_lock` contention in normal mode. Debug mode retains the legacy thread for compatibility.
- **CPU dispatch optimization**: Single direct 64K function pointer dispatch table (`inst_direct_dispatch[]`) replaces the two-level `inst_opcode_map` + `inst_instruction_to_pointer` indirection. `cpu_step()` marked `__attribute__((hot))`.
- **4-way set-associative PMMU cache**: Upgraded from direct-mapped to 4-way set-associative TLB, reducing conflict misses for workloads with aliased page addresses. Pseudo-LRU eviction per set.
- **SoftFloat 2b upgraded to 3e**: Berkeley SoftFloat library upgraded from version 2b to 3e with full backward-compatible API shim (`softfloat_compat.h`). Improved accuracy and ARM64 performance.
- **Core Audio sound output**: Apple Sound Chip (ASC) now outputs audio via Core Audio AudioQueue API — 22254 Hz stereo 8-bit unsigned PCM with proper FIFO ring buffer management and thread-safe access.

### Modernized APIs
- Replaced all `gettimeofday()` with `clock_gettime(CLOCK_MONOTONIC)` across `core_api.c`, `via.c`, `ethernet.c`
- Replaced `sys_errlist[errno]` with `strerror(errno)` in SDL GUI
- Replaced `bzero()` with `memset()` in debugger
- Ethernet receiver: replaced busy-polling `usleep(50)` with `pthread_cond_timedwait` on a dedicated condition variable, signaled when `bnry` register is updated

### SDL GUI Modernization
- Replaced all deprecated immediate-mode OpenGL rendering (`glDrawPixels`, etc.) with SDL_Renderer + SDL_Texture (`SDL_PIXELFORMAT_ABGR8888`, `SDL_TEXTUREACCESS_STREAMING`)
- Removed OpenGL dependency entirely from SDL GUI

### Debugger Rewrite
- Migrated from deprecated GLUT to SDL2 for windowing and rendering
- Fixed GLUT init order bug (display mode was set after window creation)
- Replaced hardcoded ROM/disk paths with configurable sources: command-line arguments, environment variables (`SHOEBILL_ROM`, `SHOEBILL_DISK`), and defaults
- Replaced deprecated OpenGL rendering with SDL_Renderer + SDL_Texture

## [0.1.0] - 2026-03-21

### Added
- Apple Silicon (ARM64) native support — universal binary (arm64 + x86_64)
- New SwiftUI + Metal GUI application (`ShoebillApp/`)
- Modern macOS 14+ interface with SwiftUI Settings, toolbar controls
- Metal-based framebuffer rendering replacing deprecated OpenGL
- Proper HiDPI/Retina display scaling
- Mouse capture/release (click to capture, right-click to release)
- Full ADB keyboard mapping
- PRAM persistence via Application Support directory
- C bridge helper (`shoebill_swift_helpers.h`) for Swift interop with anonymous C struct fields
- Xcode project for the SwiftUI app with "Build Core Library" script phase

### Changed
- `core/Makefile`: replaced i386 target with arm64; builds universal library
- `gui/Shoebill.xcodeproj`: updated to macOS 14.0 deployment target, `SDKROOT = macosx`, `VALID_ARCHS = x86_64 arm64`
- `core/fpu.c`: renamed `HACKY_MATH_X86` to `HACKY_MATH_NATIVE` — the `double`/`math.h` path is architecture-independent
- Top-level `Makefile`: new targets (`app`, `core`, `sdl`, `legacy-gui`, `debugger`)
- `README.md`: rewritten with updated build instructions and credits
- Version bumped to 0.1.0

### Fixed
- `core/fpu.c`: corrected `memset()` argument order in `fpu_initialize()` (issue [#20])
- `core/fpu.c`: fixed K&R-style `_native_tentox(a)` declaration to `_native_tentox(double a)` (issue [#15])
- `README.md`: fixed broken relative image URL (from PR [#19])

### Credits
- ARM64 build support based on [PR #19](https://github.com/pruten/shoebill/pull/19) by [@calmsacibis995](https://github.com/calmsacibis995)
- Bug reports by [@mitchblank](https://github.com/mitchblank) ([#15](https://github.com/pruten/shoebill/issues/15), [#20](https://github.com/pruten/shoebill/issues/20))

## [0.0.5] - 2015-09-13

Last release by original author Peter Rutenbar.
32 changes: 23 additions & 9 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,19 +1,33 @@

CC = clang
CFLAGS = -O3 -flto -Wno-deprecated-declarations
NPROC = $(shell sysctl -n hw.ncpu 2>/dev/null || echo 4)

all: shoebill
all: app

shoebill: make_gui debugger
# New SwiftUI + Metal GUI (recommended)
app: core
@if command -v xcodebuild >/dev/null 2>&1 && xcodebuild -version >/dev/null 2>&1; then \
xcodebuild -project ShoebillApp/ShoebillApp.xcodeproj -scheme ShoebillApp -configuration Release SYMROOT=build/ShoebillApp; \
else \
ShoebillApp/build.sh; \
fi

make_gui: make_core
xcodebuild -project gui/Shoebill.xcodeproj SYMROOT=build
# Legacy Cocoa GUI
legacy-gui: core
xcodebuild -project gui/Shoebill.xcodeproj SYMROOT=build/LegacyGUI

debugger: make_core
$(MAKE) -C debugger
# Core emulator library only
core:
$(MAKE) -C core -j $(NPROC)

# SDL2 cross-platform GUI
sdl: core
cd sdl-gui && ./osx_build.sh

make_core:
$(MAKE) -C core -j 4
# Debugger
debugger: core
$(MAKE) -C debugger

clean:
rm -rf intermediates gui/build
rm -rf intermediates build gui/build
117 changes: 91 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,46 +1,111 @@
<h1><img align=right src="../../../pruten.github.io/raw/master/web/stork_tiny_head3.jpg"/>Shoebill</h1>
<h1><img align=right src="https://github.com/pruten/pruten.github.io/raw/master/web/stork_tiny_head3.jpg"/>Shoebill</h1>

A Macintosh II emulator that runs A/UX (and A/UX only).
A Macintosh II emulator that runs A/UX (and A/UX only).

Shoebill is an all-new, BSD-licensed Macintosh II emulator designed from the ground up with the singular goal of running A/UX.
Shoebill is a BSD-licensed Macintosh II emulator designed from the ground up with the singular goal of running A/UX.

Shoebill requires a Macintosh II, IIx or IIcx ROM, and a disk image with A/UX installed.

[Download the latest release], and then see the [getting started] wiki.
Also check out [screenshots].
See the [getting started] wiki for setup instructions.

__Update (March 29, 2023): About issues/pull requests__
## What's New (v0.1.0)

__I just wanted to say that I appreciate some folks are still using Shoebill and submitting issues and pull requests. I wish I could continue working on this project, but there's a likely conflict of interest, and so I've mostly avoided pushing changes. I apologize for being unable to address the many, many bugs in this repo. (Also for anyone unaware, [Qemu is now able] now to run A/UX 3.x on its emulated Quadra 800.)__
This fork modernizes Shoebill for current macOS:

__Update (Sept 13, 2015): [Shoebill 0.0.5 is available]__
- **Apple Silicon (ARM64) support** — native builds for M1/M2/M3/M4 Macs (universal binary with x86_64)
- **New SwiftUI + Metal GUI** — modern macOS interface replacing the legacy Objective-C/OpenGL app, with HiDPI/Retina display support
- **Bug fixes** from community issues:
- Fixed `memset()` argument swap in FPU initialization ([#20])
- Fixed K&R-style function declaration causing build errors on modern compilers ([#15])
- Renamed `HACKY_MATH_X86` to `HACKY_MATH_NATIVE` (the `double` math path works on all architectures)
- **Build system** updated: dropped i386, targets macOS 14.0+

__This will probably be the last release. I won't be able to work on Shoebill going forward (by contractual obligation), so I wanted to race out one last release. Only an OS X binary is available, sorry, and it's very unpolished. But the SDL GUI should still build on linux/windows.__
Based on ARM64 build support from [PR #19] by [@calmsacibis995], with bug fixes reported by [@mitchblank].

## Requirements

#### Supports
* A/UX 1.1.1 through 3.1 (and 3.1.1 a little)
- macOS 14.0 (Sonoma) or later
- Xcode 15+ (for building the SwiftUI app) or Command Line Tools (for building the core library)
- A Macintosh II/IIx/IIcx ROM image
- An A/UX disk image (1.1.1 through 3.1)

## Building

### SwiftUI App (recommended, requires Xcode)

```bash
make app
```

The built application will be in `build/ShoebillApp/`.

### Core Library Only (Command Line Tools sufficient)

```bash
make core
```

Produces `intermediates/libshoebill_core.a` (universal: arm64 + x86_64).

### Legacy Cocoa GUI

```bash
make legacy-gui
```

### SDL2 GUI (cross-platform)

```bash
make sdl # macOS
cd sdl-gui && ./lin_build.sh # Linux
cd sdl-gui && win_build.bat # Windows
```

### Debugger

```bash
make debugger
```

## Supports

* A/UX 1.1.1 through 3.1 (and 3.1.1 partially)

## Currently Implements

#### Currently Implements
* 68020 CPU (mostly)
* 68881 FPU (mostly)
* 68851 PMMU (just enough to boot A/UX)
* SCSI
* ADB
* 68851 PMMU (enough to boot A/UX)
* SCSI (NCR 5380)
* ADB (keyboard/mouse)
* PRAM
* Ethernet (via emulated Apple EtherTalk/DP8390 card)
* A NuBus video card with 24-bit depth.
* NuBus video card with 24-bit depth

## Not Yet Implemented

#### Does not implement (yet)
* Sound
* Floppy
* Sound (ASC chip partially implemented)
* Floppy (IWM)
* Serial ports


[Download the latest release]:https://github.com/pruten/Shoebill/releases
[getting started]:https://github.com/pruten/Shoebill/wiki/Getting-Started
[screenshots]:https://github.com/pruten/Shoebill/wiki/Screenshots
[Shoebill 0.0.5 is available]:https://github.com/pruten/Shoebill/releases
[The thread on emaculation.com]:http://www.emaculation.com/forum/viewtopic.php?f=7&t=8288
[Qemu is now able]:https://virtuallyfun.com/2021/09/02/qemus-macintosh-quadra-in-alpha-usability-runs-a-ux/
## License

BSD 2-Clause. See [LICENSE](LICENSE).

## Credits

- Original author: [Peter Rutenbar](https://github.com/pruten) ([@pruten])
- ARM64/Apple Silicon support: [@calmsacibis995] ([PR #19])
- Bug reports and testing: [@mitchblank] ([#15], [#20])

## See Also

- [QEMU Quadra 800](https://virtuallyfun.com/2021/09/02/qemus-macintosh-quadra-in-alpha-usability-runs-a-ux/) can also run A/UX 3.x

[getting started]:https://github.com/pruten/Shoebill/wiki/Getting-Started
[#15]:https://github.com/pruten/shoebill/issues/15
[#20]:https://github.com/pruten/shoebill/issues/20
[PR #19]:https://github.com/pruten/shoebill/pull/19
[@pruten]:https://github.com/pruten
[@calmsacibis995]:https://github.com/calmsacibis995
[@mitchblank]:https://github.com/mitchblank
Binary file added ShoebillApp/AppIcon.icns
Binary file not shown.
Binary file added ShoebillApp/AppIcon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
111 changes: 111 additions & 0 deletions ShoebillApp/EmulatorViewModel.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import SwiftUI
import Foundation

@Observable
final class EmulatorViewModel {
let bridge = ShoebillBridge()
private(set) var isRunning = false
private(set) var isMouseCaptured = false
private(set) var isKeyboardCaptured = false
var errorMessage: String?

private var keyboardHandler: KeyboardHandler?
private let mouseHandler = MouseHandler()
private var fallbackVBLTimer: Timer?

var screenSlot: UInt8 = 9
var metalRendering = false

func startEmulator() {
guard !isRunning else { return }
errorMessage = nil

let defaults = UserDefaults.standard
let romPath = defaults.string(forKey: "romPath") ?? ""
let kernelPath = defaults.string(forKey: "rootKernelPath").flatMap { $0.isEmpty ? nil : $0 } ?? "/unix"
let memSize = defaults.integer(forKey: "memorySize")
let verbose = defaults.object(forKey: "verboseState") != nil ? defaults.bool(forKey: "verboseState") : true

guard !romPath.isEmpty else {
errorMessage = "ROM path is not set. Open Settings to configure."
return
}

guard defaults.string(forKey: "scsiPath0")?.isEmpty == false else {
errorMessage = "Boot disk (SCSI 0) is not set. Open Settings to configure."
return
}

var scsiPaths: [String?] = []
for i in 0..<7 {
let p = defaults.string(forKey: "scsiPath\(i)")
scsiPaths.append(p?.isEmpty == false ? p : nil)
}

let screenWidth = UInt16(defaults.integer(forKey: "screenWidth0").clamped(to: 512...2560))
let screenHeight = UInt16(defaults.integer(forKey: "screenHeight0").clamped(to: 342...1600))

let config = ShoebillBridge.EmulatorConfig(
romPath: romPath,
kernelPath: kernelPath,
ramSizeMB: UInt32(max(4, min(1024, memSize == 0 ? 16 : memSize))),
verbose: verbose,
scsiPaths: scsiPaths,
screenWidth: screenWidth,
screenHeight: screenHeight,
screenSlot: screenSlot
)

do {
try bridge.initialize(with: config)
bridge.start()
startVBLTimer()
isRunning = true
} catch {
errorMessage = error.localizedDescription
}
}

func stopEmulator() {
guard isRunning else { return }
releaseInput()
fallbackVBLTimer?.invalidate()
fallbackVBLTimer = nil
bridge.stop()
isRunning = false
}

private func startVBLTimer() {
let slot = screenSlot
fallbackVBLTimer = Timer.scheduledTimer(withTimeInterval: 1.0 / 60.0, repeats: true) { [weak self] _ in
guard let self, self.isRunning, !self.metalRendering else { return }
self.bridge.sendVBLInterrupt(slot: slot)
}
RunLoop.main.add(fallbackVBLTimer!, forMode: .common)
}

func captureInput() {
guard isRunning, !isMouseCaptured else { return }
mouseHandler.capture()
isMouseCaptured = true

if keyboardHandler == nil {
keyboardHandler = KeyboardHandler(bridge: bridge)
}
keyboardHandler?.startCapturing()
isKeyboardCaptured = true
}

func releaseInput() {
mouseHandler.release()
isMouseCaptured = false
keyboardHandler?.stopCapturing()
isKeyboardCaptured = false
}
}

private extension Comparable {
func clamped(to range: ClosedRange<Self>) -> Self {
min(max(self, range.lowerBound), range.upperBound)
}
}
26 changes: 26 additions & 0 deletions ShoebillApp/Info.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>Shoebill</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>0.1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSMinimumSystemVersion</key>
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright 2013-2015 Peter Rutenbar, 2026 Contributors. BSD 2-Clause.</string>
</dict>
</plist>
Loading