AmigaOS 4.1FE support#5
Merged
Merged
Conversation
Start separating Amiga-family OS integration from the packet handler so AmigaOS 4 can grow a native frontend without cloning the OS3 handler. Select an os3 or os4 platform subdirectory from the Makefile and add shared sys_compat declarations. Route allocation, message ports, I/O requests, signals, interrupt setup, library/interface ownership, and utility hook calls through per-OS implementations. Keep -lauto out of the OS4 link. Explicitly open dos.library, obtain IDOS, open utility.library, and obtain its main interface. Move the temporary OS4 main wrapper into the OS4 frontend directory and document the remaining OS4 porting work.
Move the remaining AmigaOS 4 source differences out of the shared packet handler. Add per-target compatibility headers and route the interrupt callback ABI, FileInfoBlock entry type, and DeviceNode lock copying through sys_compat helpers. Document the CD0 default, OD0 fallback, and requester policy for unsafe conflicts. Name fallback should be quiet, while duplicate OD0 or same physical Device/Unit conflicts should fail visibly.
Teach the OS4 entry wrapper to receive and validate the initial ACTION_STARTUP packet before passing it into the shared handler loop. Add a native FileSystemVectorPort template with explicit unsupported-vector stubs. This gives the next slice a real OS4 frontend to map onto shared read-only operations.
Move the first native OS4 vector callbacks onto shared handler operations for locks, file handles, reads, seeks, and info. Keep the packet frontend on the same operations so behavior stays aligned. Add native ExamineData allocation for object, lock, and filehandle examine calls. Leave directory iteration unsupported until the OS4 private context ownership rules are confirmed.
Wire native OS4 vector callbacks for inhibit, serialize, and lock or file mode changes into the shared handler operations. Return ERROR_DISK_WRITE_PROTECTED from mutating vectors so DOS sees a deliberate read-only filesystem result instead of missing support.
Add a shared handler operation that returns the next directory entry by resume key, matching the existing packet iteration rules. Use it from FSExamineDir to allocate native ExamineData entries, recycle stale context nodes when possible, and append results to the OS4 examine context FreshNodeList.
Make always defines CC, defaulting to "cc", so the conditional assignment "CC ?= m68k-amigaos-gcc" never took effect and a plain "make amiga" tried to build the handler with the host compiler. Assign the m68k cross-compiler only when CC still carries make's built-in default, so command-line and environment overrides such as CC=ppc-amigaos-gcc keep selecting their own toolchain unchanged. The OS4 build no longer needs -Wno-error=deprecated-declarations now that deprecated Exec and DOS calls go through the per-OS sys_compat wrappers. Remove the suppression so the build fails again if a deprecated interface member sneaks back in.
On AmigaOS 4, dos.library only calls a filesystem through its native FileSystemVectors when the handler's port validates as a vector port. The handler replied to ACTION_STARTUP with the plain process message port, so even with the vector callbacks implemented, every DOS call was forced through legacy packet emulation. Allocate the FileSystemVectorPort during startup, attach a dedicated signal to its embedded message port, and verify the object with GetFileSystemVectorPort() before relying on it. Switch the handler port to the vector port before replying to the startup packet, so the device node, volume node, locks, and the packet loop all reference the port DOS validates. If activation fails, the startup packet is rejected and the port and signal are released; on shutdown the process port is restored before the handler exits. Track the owning process port separately from the active handler port in handler_global, and introduce an ODFS_AMIGA_OS4 macro in the per-target compat headers so shared code can test the build target without scattering __amigaos4__ checks.
The SERIAL_DEBUG log sink wrote bytes to the serial port with inline m68k assembly calling exec's RawPutChar vector, which cannot build for the PPC OS4 target. Keep the raw serial path for OS3 and use exec's DebugPrintF() on OS4, which ends up in the same serial/Sashimi debug stream. This makes the SERIAL_DEBUG=1 and PACKET_TRACE=1 build variants compile for OS4.
AmigaOS 4 deprecates ACTION_DIE in favor of ACTION_SHUTDOWN (V51+), which DismountDevice() sends to ask a filesystem to remove its volume node and terminate. The handler only recognized ACTION_DIE, so an OS4 dismount would have been answered with ERROR_ACTION_NOT_KNOWN and the handler process would have stayed alive. Accept ACTION_SHUTDOWN wherever ACTION_DIE is accepted: reply DOSTRUE and leave the packet loop, which already unmounts the volume, drains outstanding locks and filehandles, and unpublishes the device node. The OS4 SDK defines no shutdown member in struct FileSystemVectors, so the packet loop remains the correct delivery path even with the native vector port active. The OS3 NDK does not define the packet number, so the OS3 target compat header supplies it; OS3 DOS never sends it, and accepting it unconditionally keeps the shared loop free of OS conditionals.
The OS4 frontend cast the portable one-argument media-change callback
directly into is_Code. On AmigaOS 4 the V50+ conventions documented
in the exec autodocs pass is_Data as the third argument:
Cause() soft interrupts:
void handler(int32 unused, struct ExecBase *sysbase,
APTR is_data);
interrupt servers (AddIntServer):
ULONG handler(struct ExceptionContext *ctx,
struct ExecBase *sysbase, APTR userData);
With the old cast, the callback's data parameter received zero (or
the exception context) instead of the change-interrupt data, so the
NULL check in the callback made the handler silently miss every
media-change event fired through TD_ADDCHANGEINT.
Install a V50-style trampoline that ignores the first two arguments
and forwards is_Data to the portable callback. Both documented
conventions place is_Data third, so one entry point covers either
delivery path. This mirrors the a1 register-binding trampoline the
OS3 frontend already uses.
The handler built its published DeviceNode and the volume DeviceList node by hand: AllocMem of the structure plus a name buffer, then filling every field. On AmigaOS 4 those are compatibility layouts whose real definitions (struct DeviceNode, struct VolumeNode) carry a StructSize field and reserved members that DOS expects the allocator to initialize; the manual path left them zero. Add odfs_amiga_create_dos_entry()/odfs_amiga_delete_dos_entry() to the per-OS compatibility layer. The OS3 implementation keeps the manual allocation, including its deliberate avoidance of MakeDosEntry() so volume names with AmigaDOS metacharacters such as parentheses are not normalized. The OS4 implementation uses AllocDosObject(DOS_DOSLIST, ADO_Type, ADO_Name), which the dos autodoc documents as the supported allocator (MakeDosEntry() itself is a stub over it since V52.16) and which fills dol_StructSize and the V52 fields. AddDosEntry()/RemDosEntry()/AttemptLockDosList() are unchanged; they remain current API on OS4.
The OS4 dospackets autodoc states that vector-port functions are invoked from the calling process context, like library calls, and that all access to filesystem state from them must be protected by a semaphore. The vector callbacks previously called the shared handler operations with no locking, racing against each other and against the handler process (packet dispatch and media-change handling) over the lock list, filehandle list, block cache, and the single device IORequest and DMA bounce buffer. Add a SignalSemaphore to the handler globals on OS4. Every vector callback that touches handler state now holds it across the shared operation, and the handler process takes it around media-change handling and shutdown teardown. Pure stub callbacks that only return an error do not take it. Direct legacy packets are now routed through the DOSEmulatePacket() function that AllocDosObject() installs in the vector port, as the autodoc prescribes. The emulator performs the equivalent vector call under the same serialization as native DOS callers, so the handler process no longer mutates filesystem state through a second unsynchronized dispatch path. Packets are validated (non-null, dp_Link == msg) before being trusted, since hand-built packets and private messages can reach the port directly. The shared handle_packet() dispatch remains the OS3/AROS path and the OS4 fallback if the vector port is absent. Shutdown now follows the documented sequence: invalidate the vector port by zeroing FSV.Version so dos.library stops vectoring new callers, tear down DOS-visible state while holding the semaphore so in-flight calls finish first, and reply the shutdown packet only after teardown. This also moves the OS3 ACTION_DIE reply after teardown, which matches common classic handler practice.
The OS4 filesystem handler must receive ACTION_STARTUP as its first process message. Newlib startup consumes that message while probing for a Workbench launch, which can deadlock the boot mount. Link the handler without C runtime startup, provide a small _start entry that initializes the Exec interface, and add the Kickstart Resident plus FileSysEntry registration used when the module loads from Kickstart.zip. Provide the small libc surface the freestanding link still needs and translate legacy allocation flags before passing them to AllocVecTags().
Diskboot starts one filesystem handler process per CD unit. The media callback context was static, so a later process could replace the device binding used by an earlier mounted volume. Move the media callback context into handler_global and pass that per-process instance to the core media layer. Reinitialize the same context after media-change remounts.
Some OS4 device paths report phantom ATAPI units that open but hang when probed through SCSI commands. Mounting those units publishes a dead volume that DOS can keep polling. Answer allocation failures on the startup packet, remove the TEST UNIT READY probe, and use TD_GETGEOMETRY before publishing the volume. Only issue MODE SELECT after geometry proves that a usable drive is present.
Workbench and native OS4 filesystem vectors expect lock objects with the OS4 DOS lock layout. The classic handler embedded a FileLock in its private lock wrapper, which leaves OS4-only fields uninitialized. Store a DOS-allocated struct Lock in each ODFS lock on OS4, keep the classic embedded FileLock on OS3, and convert through fl_FSPrivate1. Free the DOS lock when the ODFS lock is released or drained.
Workbench exercises native filesystem vectors more strictly than shell packet paths. Several callbacks returned classic packet values, left fh_Arg1 empty, or allocated ExamineData outside the directory context that DOS owns. Return native boolean SameLock and SameFile values, preserve fh_Arg1 together with fh_Arg2, implement FileSystemAttr queries, bind ExamineDir data to the DOS context, and reject inactive directory locks before iteration.
OS4 still sends legacy packet APIs such as Lock(), Examine(), and ExNext() to the handler port. Routing those packets through DOSEmulatePacket returns vector-era semantics and can reject classic examine actions that callers still use. Leave direct packets on the shared packet dispatcher and serialize that dispatcher with the OS4 filesystem semaphore. Native vector calls and legacy packets now share the same handler state without using the vector packet emulator as an extra dispatch path.
Native OS4 vector callbacks run in the caller task, but the handler reused its device IORequest whose reply port belongs to the handler task. If a caller-task DoIO() waited for completion, the device signaled the wrong task and Workbench could hang while opening a drawer. Record the handler task at startup. When media reads run from another task, allocate a temporary reply port and IORequest for that caller while reusing the same device and unit binding.
Add explicit AmigaOS 3 and AmigaOS 4 build instructions to the README. Describe compiler target selection, separate build directories, explicit tool overrides, and per-target size limits.
Add a PPC CI job that runs in the amigappc-gcc container. Put /opt/amigappc/bin on PATH, verify ppc-amigaos-gcc, and build both release and serial-debug handler variants.
Split the draft release workflow into OS3 and OS4 builders so each target uses its matching container and toolchain. Download both artifact sets before creating the draft release, and attach the PPC release and serial-debug handlers alongside the OS3 files.
Vector callbacks can run on a caller task, so reusing the handler task device request makes DoIO() wait on the wrong reply port. The previous path avoided that by allocating a temporary request for each read, but paid that allocation cost on every sector batch. Keep one cached vector I/O request per caller task in the handler global state. Refresh the device and unit from the handler request before reuse and free the cached request during shutdown.
FSExamineObj only needs metadata for the requested object, but it was going through the lock path and then immediately freeing the lock. That adds DOS lock churn to vector examine calls. Split path resolution into a shared node resolver and use it from the OS4 vector path. The packet lock path still allocates real locks, while FSExamineObj formats metadata directly from the resolved node.
The packet and OS4 vector frontends were formatting name, type, date, protection, and comment metadata independently. Keeping those paths separate made it easy for root names and Amiga AS metadata to drift. Introduce a shared node-info filler for handler metadata. Use it from FIB, ExAll, and ExamineData formatting so both frontends apply the same key, protection, date, size, comment, and root-volume name rules.
The OS4 vector template and packet emulation helpers no longer had any callers after direct packet handling moved back to the classic dispatcher. Leaving them exported made the vector port API look broader than it is. Remove the unused declarations and definitions. The vector module now only exposes allocation, teardown, and invalidation entry points.
Two sys_compat helpers only wrapped field assignments that are either OS3-only or no-ops on OS4. Keeping them in the compatibility layer added extra exported surface without hiding any real platform difference. Assign fib_EntryType and dn_Lock directly at the call sites under the OS3 guard, then drop the OS3 and OS4 helper implementations.
Each backend copied file data one sector at a time through the block cache, even for long aligned reads. That creates many media calls and duplicates the same edge-sector logic across every backend. Add odfs_cache_read_bytes() to handle partial edge sectors through the cache and send aligned multi-sector runs directly to the media layer. Switch ISO, Joliet, UDF, HFS, and HFS+ reads to that helper and cover the batching behavior with cache unit tests.
Classic ExNext must leave fib_DiskKey as the visible directory or object key, so the handler was rescanning from the start of the directory each time. Large Workbench drawers then spend most of their time replaying entries already seen. Keep a private ExNext cursor on each OS3 lock, with a root cursor for null-lock scans. The cursor stores the backend resume offset and CDDA injection state while preserving the public fib_DiskKey contract.
Duplicate-name fixing remembered every name with one allocation for the list entry and another for the copied string. Directory scans with many entries therefore spent unnecessary time in allocator bookkeeping. Add a small chunk allocator to the namefix state and store each entry with its name buffer in that pool. Destroying the state now releases the chunks in one pass.
Cache hits were found with a linear scan over every cache entry, and the high-water mark was recomputed by scanning again after each fill. Larger cache sizes made common lookups scale with the whole cache capacity. Add a simple LBA hash table with per-entry collision links and track the valid-entry count directly. Reads now find hits through the hash table and update the chains when entries are evicted or flushed.
Parent lookup for locks and file handles still had to reconstruct the next ancestor when the caller walked upward more than one level. That kept repeated ParentDir() and path ascent operations tied to directory rescans. Store an optional grandparent node in each shared object entry and carry that ancestor through path resolution, lock duplication, open, and parent queries. Fall back to parent-node resolution only when the cached ancestor is not available.
Maintaining dl_LockList by rebuilding it from the global lock list makes every lock allocation and free scan all active locks. That cost grows with open drawer state and duplicates ordering work that can be maintained incrementally. Keep a per-volume lock chain with previous and next links in each lock. Link and unlink locks as they are created, duplicated, freed, or drained, and update the DOS-visible volume lock list head inside the same critical section.
The current ROM-profile handler is about 37 KiB on this branch, so the 32 KiB ceiling fails the release and archive build paths. Raise the default budget to 40 KiB and update the ROM profile docs to match the build setting.
Add a release-published workflow that rebuilds the OS3 and OS4 LHA packages from v-tagged releases and uploads both to Aminet. The workflow skips prereleases, prepares the matching Aminet readme for each archive, and lets the Aminet action inject the release version.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.