Skip to content

Feature AmigaOS 3 - #207

Draft
MBeijer wants to merge 105 commits into
SDL2from
feature/AmigaOS3
Draft

Feature AmigaOS 3#207
MBeijer wants to merge 105 commits into
SDL2from
feature/AmigaOS3

Conversation

@MBeijer

@MBeijer MBeijer commented Aug 18, 2026

Copy link
Copy Markdown
Member

No description provided.

madebr and others added 30 commits July 5, 2026 02:55
We don't care _what_ the `sizeof` results are, we just want it to vaporize
the condition's code out of the build. So we check it with a `? 1 : 0`, so it
always boils down to a `sizeof (int)`, no matter what data type the condition
actually is.

This works with bitfields, pointers, anything that can be "truthy." If it
isn't truthy, it would fail when assertions are enabled anyhow.

Fixes libsdl-org#15990.

(manually cherry picked from commit daf519a)
This modern navigator.mediaDevices.getUserMedia has been
available since Chrome 53 / Firefox 38 / Safari 11.

See emscripten-core/emscripten#27347
the rest of the calls were updated in aec86ba but one was missed
Seed the repo with:
- Directory structure matching PDR-008 vision (all backend dirs)
- Phase 0 stub source files for all backends (video/audio/thread/timer/
  joystick/filesystem/loadso/haptic/main)
- Makefile for bebbo-gcc cross-compilation via Docker
- CLAUDE.md with SDL2-specific project instructions
- README.md with phase plan, hardware requirements, architecture
- VISION.md (PDR-008 from amiport — founding design document)
- Amiga knowledge base: ADCD 2.1 reference, Amiga Intern, M68000 PRM,
  crash patterns, 68k hardware reference, libnix reference
- Claude rules: amiga-coding, known-pitfalls, capture-learning
- Phase 0 milestone test (examples/test_init.c)
- .gitignore for build artifacts

Phase 0 milestone: SDL_Init() returns 0, SDL_Quit() doesn't crash.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
From amiport, bring over the complete test and build infrastructure:

Toolchain:
- bebbo-gcc and VBCC Dockerfiles
- Cross-compiler wrapper scripts (gcc, ar, ranlib, strip, vamos)
- Target profiles for 68000/68020/68040/PPC
- Setup and detection scripts

FS-UAE test harness:
- test-fsemu.sh (43KB main orchestrator)
- inject-keys.sh (host-side keystroke injection via osascript)
- verify-screen.py (pyte-based screen verification)
- ARexx test-runner.rexx and run-with-timeout.rexx templates
- NEW: sdl2-test.fs-uae config with RTG (graphics_card=uaegfx,
  16MB VRAM, 16MB Fast RAM, 800x600 window)

Test utilities (pre-built Amiga binaries):
- KeyInject (AddIEvents keystroke injector)
- ScreenRead (ConUnit cursor reader via FS-UAE trap)
- UAEQuit (clean FS-UAE exit from Amiga side)

Debug tools (pre-built):
- Enforcer, MungWall, SegTracker, bgdbserver

Scripts:
- doctor.sh, install-emu.sh, setup-emu.sh
- check-test-coverage.sh, serial-capture.py, debug-report.py

Documentation:
- TESTING.md: SDL2-specific testing guide (RTG differences, phases)
- HARDWARE-LANDSCAPE.md: PDR-009 hardware context

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Claude Code configuration:
- .claude/settings.json with permissions, safety hooks, auto-mode config
- Hooks: block-direct-gcc, enforce-adcd-lookup, verify-before-stop,
  check-toolchain (SessionStart)
- SDL2-specific WebFetch domains: wiki.libsdl.org, www.libsdl.org

Agents (from amiport, for reuse):
- hardware-expert: RTG card capabilities, SAGA vs CGX, AGA constraints
- build-manager: Docker cross-compilation, linker issue resolution

Reference docs:
- autodocs/ (1.5MB): exec, dos, intuition, graphics, timer, input,
  commodities, and 15 more AmigaOS library autodocs
- arexx-reference.md: ARexx syntax (test harness uses ARexx)
- newlib-availability.md: libc function availability

Standards:
- docs/test-coverage-standard.md: test quality requirements
- docs/adr/022-c99-compiler-support.md: C99 on bebbo-gcc (SDL2 requires C99)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Vendored from github.com/libsdl-org/SDL (SDL2 branch, commit f9e8203).

- include/SDL2/: 94 public headers
- src/: 473 .c files, 1090 total files

Zero modifications to upstream source. This is the clean baseline.
Our AmigaOS 3.x backend files (previously in src/*/amigaos3/) were
removed -- they had wrong signatures and will be recreated in the
next commit with real SDL2 bootstrap API.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- include/SDL2/SDL_config_amigaos3.h: Platform config header for
  AmigaOS 3.x on 68030+. Enables CyberGraphX video, AHI audio,
  Exec Tasks threading, ReadEClock timer, AmigaOS filesystem.
  Disables haptic, sensor, HIDAPI, loadso, power, locale.
  Sets SDL_DYNAMIC_API=0 (no dlopen), SDL_BIG_ENDIAN.

- include/SDL2/SDL_config.h: Added #elif __AMIGAOS3__ route to
  our config header, before the minimal fallback.

- include/SDL2/SDL_platform.h: Added __AMIGAOS3__ detection using
  bebbo-gcc's predefined __AMIGA__ macro (excluding OS4).

- src/atomic/SDL_spinlock.c: Added 68020+ CAS (compare-and-swap)
  instruction path for SDL_AtomicTryLock. Hardware atomic on
  single-core 68k, no OS calls needed.

- TODOS.md: Created with items from eng + CEO review.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
libSDL2.a compiles! 117 source files, 1.2 MB, zero errors.

Backend stubs with real SDL2 bootstrap API:
- src/video/amigaos3/: VideoBootStrap OS3_bootstrap (stub VideoInit)
- src/audio/amigaos3/: AudioBootStrap OS3AHI_bootstrap (stub, returns false)
- src/joystick/amigaos3/: SDL_JoystickDriver SDL_AMIGAOS3_JoystickDriver
- src/timer/amigaos3/: SDL_GetTicks64/SDL_Delay stubs
- src/filesystem/amigaos3/: SDL_GetBasePath/SDL_GetPrefPath stubs
- src/thread/amigaos3/: Phase 0 single-task stubs (mutex, sem, cond, TLS)
- src/main/amigaos3/: Empty (SDL_main placeholder)

Upstream patches:
- src/video/SDL_video.c + SDL_sysvideo.h: Register OS3_bootstrap
- src/audio/SDL_audio.c + SDL_sysaudio.h: Register OS3AHI_bootstrap
- src/joystick/SDL_joystick.c + SDL_sysjoystick.h: Register SDL_AMIGAOS3_JoystickDriver
- src/thread/SDL_thread_c.h: Add SDL_THREAD_AMIGAOS3 include path
- src/dynapi/SDL_dynapi.h: Add __AMIGAOS3__ -> SDL_DYNAMIC_API=0

Structural changes:
- Headers moved from include/SDL2/ to include/ (matches upstream layout)
- Makefile: complete 117-file source list, -m68030, Docker cross-compile

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
SDL2 for AmigaOS 3.x Phase 0 is COMPLETE.

Test results (vamos -C 68020):
  PASS: SDL_Init(0) returns 0
  PASS: SDL_Quit() after Init(0)
  PASS: SDL_GetError() non-NULL
  PASS: SDL_Init(0) idempotent (double init)
  PASS: SDL_Quit() without prior Init
  PASS: SDL_Init(SDL_INIT_VIDEO) doesn't crash (returns -1, expected)
  PASS: SDL_CreateMutex() non-NULL
  PASS: SDL_LockMutex() returns 0
  PASS: SDL_UnlockMutex() returns 0
  PASS: SDL_DestroyMutex() didn't crash
  11/11 tests passed

Fixes applied during compilation:
- SDL_config_amigaos3.h: HAVE_* defines that are "not available" must
  be UNDEFINED, not defined to 0. SDL2 checks with #if defined().
- SDL_atomic.c: Disable GCC atomic builtin auto-detection for __AMIGAOS3__
  (builtins generate __sync_* external calls that don't exist on 68k).
  Falls through to EMULATE_CAS path using our CAS-based spinlocks.
- Makefile: Added yuv2rgb/yuv_rgb_std.c and joystick/controller_type.c
  (missing from initial enumeration).

Binary: test_init 1.1 MB (statically linked with libSDL2.a 1.2 MB)
Run: vamos -C 68020 -s 32 -m 8192 examples/test_init

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace Phase 0 single-task stubs with real AmigaOS Exec primitives:

- SDL_sysmutex.c: SignalSemaphore (InitSemaphore/ObtainSemaphore/
  AttemptSemaphore/ReleaseSemaphore). Supports recursive locking
  via ss_NestCount. Direct mapping to SDL2 mutex API.

- SDL_syscond.c: Exec Signals + SignalSemaphore waiter list.
  Each waiter allocates a signal bit (AllocSignal), adds itself to
  a protected wait queue, then Wait()s. CondSignal wakes first waiter,
  CondBroadcast wakes all. Max 32 concurrent waiters.

- SDL_syssem.c: Counting semaphore via SignalSemaphore + counter +
  signal-based blocking. Fast path (count > 0) is lock-free.
  When count == 0, waiters block on allocated signal bits.

- SDL_systhread.c: CreateNewProc() with NP_Entry for thread creation.
  Thread data passed via tc_UserData. SDL_ThreadID returns FindTask(NULL).
  WaitThread uses polling (proper signal-based wait is TODO).

- SDL_systls.c: Task-keyed linked list protected by SignalSemaphore.
  FindTask(NULL) is O(1) on AmigaOS, so lookup is fast.

- SDL_systhread_c.h: SYS_ThreadHandle is now struct Task* (was int).

All 11 tests pass on vamos -C 68020. Mutex operations use real
hardware semaphores. Thread creation untested on vamos (needs FS-UAE).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The library compiles with -m68030 (our minimum target). vamos uses
-C 68020 because it has no -C 68030 mode -- the 68030 integer ISA
is identical to 68020 (differences are MMU/cache, not instructions).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Phase 0 Bootstrap is COMPLETE. README now shows:
- Subsystem boot matrix (what works, what's stubbed)
- 68030 minimum target (was 68020, corrected)
- CyberGraphX only for Phase 0-1 (P96 compatibility later)
- Accurate build/test commands including vamos flags
- Phase roadmap with current status

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Four new agents for the multi-agent workflow:

- librarian: Knowledge base builder. Fetches API docs from toolchain,
  amiport KB, and online sources. Transforms to markdown in docs/references/.
  Also maintains the skills that surface docs to other agents.
  Sources: bebbo-gcc headers, ADCD, CyberGraphX SDK, AHI docs, amiport KB,
  SDL 1.2/SDL2 OS4 reference code.

- sdl2-backend-developer: Writes C code for video (CyberGraphX), audio
  (AHI), threading (Exec Tasks), timer, filesystem, joystick backends.
  Knows SDL2's bootstrap/driver registration API and AmigaOS coding rules.

- perf-optimizer: Optimizes backend code for 68k hardware. Knows instruction
  timing, CyberGraphX blit costs, AHI callback constraints, cache behavior.
  Adapted from amiport's perf-optimizer with SDL2-specific hot paths.

- memory-checker: Verifies memory safety of backend code. Checks SDL_malloc/
  SDL_free pairing, AmigaOS resource lifecycle (OpenLibrary/CloseLibrary,
  AllocSignal/FreeSignal), and thread primitive cleanup.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implements the CyberGraphX/Picasso96 video backend for SDL2 on AmigaOS 3.x.
First successful WritePixelArray blit of ARGB32 framebuffer to an RTG screen.

Video backend (new files):
- SDL_os3window.c: Intuition window management, WB RTG screen detection,
  custom RTG screen fallback for AGA Workbench
- SDL_os3framebuffer.c: SDL_PIXELFORMAT_ARGB8888 surface with WritePixelArray
  blit via RECTFMT_ARGB to RastPort
- SDL_os3events.c: IDCMP event pump with 120-entry rawkey-to-SDL_Scancode
  table, mouse button/motion, window focus events

Critical bug fixes:
- SDL_systls.c: Lazy InitSemaphore in GetTLSData/SetTLSData -- fixes deadlock
  on real AmigaOS where ObtainSemaphore on zero-filled BSS hangs (vamos is
  lenient but FS-UAE/real hardware deadlocks)
- SDL_spinlock.c: Replace CAS inline asm with Forbid/Permit -- simpler and
  correct for single-core cooperative AmigaOS
- SDL_systimer.c: SDL_Delay now calls dos.library Delay() for real task yield
  instead of no-op (required for cooperative multitasking)
- SDL_os3video.c: Fallback to Picasso96API.library when cybergraphics.library
  not available, requester suppression via pr_WindowPtr

Test infrastructure:
- test_video.c: Visual test with RED/GREEN/BLUE/WHITE quadrants + two windows
- test_bare.c: No-SDL printf test for binary execution verification
- test_spinlock.c: Step-by-step atomic/TLS/SDL_Init diagnostic
- sdl2-test.fs-uae: A4000/040 config with Zorro III, uaegfx RTG, 128MB RAM
- test-video.sh: Build + launch workflow

Reference docs:
- cybergraphx-reference.md: 1184-line CyberGraphX V4 API reference
- sdl2-video-contract.md, sdl2-events-contract.md: SDL2 backend interface docs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…tructure plan

Includes Phase 1.5 test state machine design for autonomous FS-UAE testing,
all 6 phases with completion status, and key bugs fixed reference table.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… capture-learning

Agent changes:
- hardware-expert: opus, added vamos vs FS-UAE platform difference checklist
- sdl2-backend-developer: opus, added Phase 0-1 bug learnings to prevent repeats
- memory-checker: upgraded haiku -> sonnet (AmigaOS memory bugs are fatal)
- test-designer: NEW sonnet agent for Phase 1.5 automated test infrastructure

New skills:
- /capture-learning: routes bugs/pitfalls to crash-patterns, known-pitfalls,
  memory, agent prompts, hooks, or reference docs
- /fsemu-setup: FS-UAE + P96 RTG troubleshooting quick reference

CLAUDE.md additions:
- "Agents and Skills -- When to Use What" decision tree
- Agent summary table with model assignments
- Skill summary table
- Mandatory workflow: research -> implement -> build -> test -> audit

Also adds existing skills to version control:
- /amiga-api-lookup, /rtg-api-lookup, /sdl2-api-lookup
- /sdl2-build, /sdl2-test
- .claude/rules/sdl2-backend.md

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
All 7 agents now have memory: project for cross-session learning.
No functional changes -- just configuration.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Each agent now has relevant skills preloaded at startup:
- sdl2-backend-developer: amiga-api, rtg-api, sdl2-api, sdl2-build
- hardware-expert: amiga-api, fsemu-setup
- librarian: amiga-api, rtg-api, sdl2-api (+ WebSearch tool)
- memory-checker: amiga-api, capture-learning
- perf-optimizer: amiga-api, rtg-api, sdl2-build
- test-designer: sdl2-test, sdl2-build, fsemu-setup
- build-manager: already had skills from earlier

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…cing sprite

Test infrastructure (Phase 1.5):
- make test: vamos smoke tests (4 tests, ~2s)
- make test-fsemu: FS-UAE RTG suite with ARexx harness (7 tests, ~18s)
- Data-driven test list (tests.txt single source of truth)
- Generated ARexx harness from tests.txt (OPEN/WRITELN pattern)
- Sentinel-based completion detection, TAP output, PID cleanup

Timer (ReadEClock):
- SDL_GetTicks64 via timer.device UNIT_ECLOCK (709 KHz PAL)
- SDL_GetPerformanceCounter/Frequency return real EClock values
- CreateMsgPort + CreateIORequest + OpenDevice lifecycle
- Graceful fallback on vamos (no timer.device)
- NOTE: Uint64 return values arrive as 0 at caller despite correct
  internal computation (crash-patterns #24, under investigation)

Video cleanup:
- Removed debug printf/fflush from SDL_os3video.c and SDL_os3framebuffer.c
- Removed unused #include <stdio.h>

Phase 2 events:
- test_events verifies IDCMP event pump (focus_gained confirmed)
- test_sprite: bouncing 32x32 rect at ~50fps validates full pipeline

New pitfalls documented:
- ARexx OPEN('A') does not create files
- ARexx Echo with embedded > is unreliable
- vamos -s flag is in KiB not bytes

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Root cause: SDL_TicksInit() was only called from SDL_InitMainThread()
(triggered by SDL_CreateThread), not during SDL_Init(). Programs that
never created threads had timer_initialized=0 forever.

Fix: Add lazy init to SDL_GetTicks64, SDL_GetPerformanceCounter, and
SDL_GetPerformanceFrequency -- same pattern as Unix/Windows backends.
First call to any getter triggers SDL_TicksInit() automatically.

Results on FS-UAE (A4000/040 PAL):
  SDL_GetTicks64() = 108 ms after Delay(100) -- correct
  SDL_GetPerformanceFrequency() = 709379 Hz -- real PAL E-Clock
  SDL_GetPerformanceCounter() increases between calls

Also works on vamos (timer.device emulated):
  SDL_GetTicks64() = 103 ms after Delay(100) -- correct

Crash-patterns #24 updated: was misdiagnosed as Uint64 ABI corruption.
Added test_uint64.c to verify Uint64 returns work correctly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
AHI audio backend (src/audio/amigaos3/SDL_os3audio.c):
- Device interface with CMD_WRITE double-buffering for gapless playback
- Two AHIRequest structs sharing one MsgPort, chained via ahir_Link
- Format negotiation: S16MSB (native 68k), S8 fallback
- Probe-based init: opens/closes ahi.device to check availability
- Clean shutdown: AbortIO+WaitIO, CloseDevice, FreeMem
- Minimal AHI defs in header (bebbo-gcc lacks devices/ahi.h)

Timer lazy init fix (crash-patterns #24 RESOLVED):
- SDL_GetTicks64/PerfCounter/PerfFreq now call SDL_TicksInit() on
  first access, matching Unix/Windows SDL2 pattern
- Root cause: SDL_TicksInit only ran from SDL_CreateThread, not SDL_Init
- Timer now works on both vamos and FS-UAE: 709379 Hz PAL E-Clock

Tests: 9/9 passing (test_audio gracefully skips when AHI not installed)
AHI installation files staged at build/amiga/AHI-Install/

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
bdgscotland and others added 22 commits August 18, 2026 10:28
- VERSION file (semver, single source of truth)
- CHANGELOG.md (Keep a Changelog format, backfills v0.5.0)
- scripts/release.sh (packages SDK + ports, tags, gh release create)
- Makefile: release and release-dry-run targets

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…dvar)

include/cxx/amiga_cxx_compat.h provides:
- std::mutex, std::recursive_mutex (wraps SDL_mutex)
- std::thread (wraps SDL_CreateThread)
- std::condition_variable (wraps SDL_cond)
- std::lock_guard, unique_lock, scoped_lock
- std::round/lround/hypot in std namespace
- std::clamp/min/max mixed int/long overloads for 68k

include/cxx/endian.h provides big-endian macros for 68k.

These shims are needed because -noixemul disables pthreads,
which causes libstdc++ to gate out all threading primitives.
The SDL2-backed implementations work via our Exec Tasks backend.

Usage: -I$(SDL2_ROOT)/include/cxx -include amiga_cxx_compat.h

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Try LockBitMapTags for direct VRAM writes when blitting without scaling.
Falls back to WritePixelArray if lock fails (e.g., uaegfx on FS-UAE).
Logs which path is active on first frame for debugging.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…lean gitignore

- Add LockBitMapTags direct VRAM path in UpdateWindowFramebuffer
- Add SDL_os3double.c (17 double-precision soft-float functions for 68k)
- Add C++ compatibility shims (mutex, thread, condvar)
- Document SDL_Renderer 5-copy bottleneck and bypass (328ms -> 23ms)
- Clean .gitignore: tar.gz artifacts, vendored ports, Claude local files
- Bump VERSION to 0.7.0

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Replace header SVG with pixel art logo (Amiga + SDL2 + subsystem icons)
- Add Master of Orion 1 screenshot as hero image
- Update README to list all 4 running ports with release links

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
SDL_SemWaitTimeout ignored the timeout parameter and called Wait()
which blocks forever. This broke SDL_AddTimer -- the timer thread
called SemWaitTimeout(sem, 16ms) but never returned, so timer
callbacks never fired. Games using SDL_AddTimer (like SDLPoP with
USE_COMPAT_TIMER) hung after the first frame.

Fix: for finite timeouts, poll with SDL_Delay(1) loop. For infinite
waits (SDL_MUTEX_MAXWAIT), use the existing Wait() signal path.

Also adds render timing instrumentation (SDL_OS3_DEBUG).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
FS-UAE's uaegfx RTG card reports PIXFMT_BGRA32 (12) for its VRAM
pixel format. Our LockBitMapTags fast path was doing a raw memcpy of
ARGB8888 surface data into BGRA32 VRAM, resulting in swapped R/B
colors (blue instead of red/orange).

Fix: bswap32 each pixel before writing to VRAM in the LockBitMap
path, and swap back after to keep the SDL surface data correct.
Also applies to both WritePixelArray fallback paths.

Discovered while porting Command & Conquer (Vanilla Conquer) which
uses palettized 8-bit surfaces -- the blue nuclear explosions were
the telltale sign.

Also removes the Stop hook from settings.json (was auto-firing
and interrupting active debugging sessions).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace 3-pass approach (convert entire frame, memcpy, convert back)
with single-pass inline bswap32 during the memcpy. ~3x faster.

Also: detect VRAM pixel format dynamically via LBMI_PIXFMT instead
of always assuming BGRA. Only converts when bm_pixfmt != PIXFMT_ARGB32.
Remove unnecessary conversion from WritePixelArray paths (WPA handles
format conversion internally via RECTFMT_ARGB).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Detect AGA chipset via GfxBase->ChipRevBits0 when CyberGraphX is
unavailable (or P96 library present without a card). Opens native
320x200 8-bit AGA screen using BestModeID + OpenScreenTags.

Key changes:
- VideoInit: graceful fallback from RTG to AGA with chipset detection
- SetDisplayMode: no-op for AGA (screen lifecycle in window code)
- OpenScreen: refactored shared helper for both RTG and AGA paths
- Framebuffer: INDEX8 surface + WriteChunkyPixels (system c2p)
- Palette: LoadRGB32 sync via SDL_GetWindowSurface palette object
- New SDL_os3aga.c/h: palette helper (LoadRGB32 table builder)

Tested: C&C (Vanilla Conquer) runs on FS-UAE A4000/040 without RTG
card, correct colors and parity with RTG display path.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
WriteChunkyPixels (system c2p) measured 189ms/frame on emulated 030,
77ms on 040 real speed. Kalms c2p1x1_8_c5_gen (public domain, 68020+
asm) measures 2ms on 040 max speed, ~90ms on 030 real speed.

The 030 real-speed improvement is limited by CHIP RAM bus contention
(bitplane writes compete with AGA display DMA), not CPU. On PiStorm
or fast emulators, the c2p is essentially free.

- Add c2p1x1_8_c5_gen.s (Kalms, public domain, assembled by vasm)
- Add OS3_AGA_C2P/C2PInit wrappers in SDL_os3aga.c
- Replace WriteChunkyPixels call in UpdateWindowFramebuffer
- Add vasm build rule to Makefile (-Fhunk for amiga object format)
- vasmm68k_mot available in Docker image and via Homebrew

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
IsCyberModeID() works via P96 emulation on Vampire SAGA, but
BestCModeIDTags() can fail. Add OS3_FindRTGMode() manual mode
walker using DIPF_IS_FOREIGN flag as fallback. Also fix WB
screen detection to check bitmap depth when GetCyberMapAttr
returns 0 on P96-only systems.

Tested on real A2000 + Vampire V2 Gold with SAGA RTG:
- 11/11 SDL2 tests pass
- Window creation works (fullscreen 640x480)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
SetDisplayMode was missing the OS3_FindRTGMode fallback that was
already in OpenFullscreen. Promoted OS3_FindRTGMode from static to
shared via SDL_os3window.h so both modules can use it.

Also improved windowed WB screen detection to check bitmap depth
as a proxy for RTG when GetCyberMapAttr returns 0.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
bebbo-gcc's __builtin_clz always returns 0 on 68k, causing
SDL_MostSignificantBitIndex32 to map every subsystem to index 31.
This made all SDL_Init subsystem refcounts share one slot, so
initializing audio prevented video from initializing (ShouldInit=0).

Discovered on real A2000+Vampire V2: C&C called SDL_InitSubSystem
(AUDIO) then SDL_Init(VIDEO) which silently skipped VideoInit.

Fall through to the portable bit-twiddling implementation instead.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…-015 OpenTTD)

Two interlocking changes that take libSDL2-amigaos3 RTG render perf
through the floor for any consumer port that hits the LockBitMap path.

== 1. SDL_os3framebuffer.c: 68k inline asm bswap32 ==

MemcpyARGB_to_BGRA was pure C with mask+shift+OR (4 mask + 4 shift + 3
OR per pixel = ~25 cycles). Replaced with __GNUC__ __asm__ inline:

    "rol.w  #8, %0\n\t"   // swap inner two bytes
    "swap   %0\n\t"       // swap word halves
    "rol.w  #8, %0"       // swap inner two bytes again

~12 cycles per pixel on 68030. Combined with -O2 build (vs Makefile -O0
default) the per-frame memcpy of a 1.2 MB framebuffer drops from 77 ms
to 12 ms = 6.2x speedup. Verified with ReadEClock instrumentation in
OpenTTD 13.4.

Pure-C fallback retained under #else for non-GCC builds.

== 2. SDL_os3window.c: remove +64 padding in OS3_OpenWindowed ==

When no Workbench public screen is available (CLI-only boot), libSDL2
opens its own RTG screen for the SDL window. Old code padded the
requested size by +64 ("scrw = window->w + 64") and minimum-clamped to
640x480. BestCModeIDTags then rounded UP to the next standard Picasso96
mode (640+64=704 picked 800x600).

Result: the AmigaOS Window->Width was 800 but the SDL surface stayed at
640x480. In OS3_UpdateWindowFramebuffer this triggered need_scale=1 and
forced the slow BitMapScale + WritePixelArray path on every frame
instead of the fast LockBitMap memcpy path. ~50 ms / frame penalty.

Patched to request EXACT window->w / window->h. Picasso96 picks the
matching mode (640x480 if asked) and need_scale=0 so the fast path
engages. Documented in pitfall.

== ALSO: SDL_OS3_PROFILE instrumentation block ==

Added optional ReadEClock-based profiling around LockBitMapTags, the
memcpy/bswap loop, UnLockBitMap, and the WritePixelArray fallback.
Writes WORK:OpenTTD-SDL2/sdl2-perf.log via direct AmigaDOS Open/Write/
Close (bypasses libnix stdio buffering). Captures fast-path-vs-fallback
counts, bm_pixfmt, needs_swap, per-call timing. Gated on -DSDL_OS3_PROFILE
so production builds incur zero overhead.

Plus a one-shot heartbeat sentinel and a sdl2-branch.log diagnostic
(prints win_w/win_h/surface_w/surface_h on first call) used to confirm
the +64 padding fix landed correctly.

== AMIGA-KB PITFALLS CAPTURED ==

Three universal pitfalls written to amiga-kb during this session:

- "libSDL2-amigaos3 OS3_OpenWindowed +64 screen padding forces
  BitMapScale slow path" (high)
- "68k inline asm bswap32 (rol.w/swap/rol.w) is 6x faster than C
  mask+shift+OR for ARGB-BGRA conversion" (high)
- "libSDL2-amigaos3 Makefile defaults to -O0 -- perf trap for any
  consumer port" (medium)

Cross-referenced in amiport repo .claude/rules/known-pitfalls.md.

== VERIFIED ==

OpenTTD 13.4 (PDR-015, 2026-04-18/19). After both patches + libSDL2 -O2:
- SDL_UpdateWindowSurfaceRects: 54 ms (BitMapScale path) -> 12 ms
  (LockBitMap memcpy with asm bswap)
- LoopOnce: 70.7 ms -> ~24 ms at 640x480 (~42 fps theoretical)
- Game reaches title menu cleanly, no visual regressions

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Changed:
- Extend SDL CMake platform handling for AmigaOS 3, AmigaOS 4, and WarpOS.
- Regenerate native ABI definitions and export tables from SDL dynapi metadata.
- Allow this fork's AI-assisted workflow.

Fixed:
- Complete the AmigaOS 3 AHI playback and capture request lifecycle.
- Make the shared SDL configuration template compile with the Amiga toolchains.
- Correct native library exports and AmigaOS 4 build compatibility.

Added:
- Add CPM-pinned cmake-amiga-common-library and SFDC dependencies.
- Add CMake targets for native OS3 and OS4 libraries plus OS4 shared objects.
- Add WarpOS toolchain modules and Docker-oriented Amiga build documentation.

Removed:
- Remove the fork's blanket AI and LLM contribution prohibition.
@MBeijer
MBeijer marked this pull request as draft August 18, 2026 13:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.