diff --git a/crates/volamos-core/src/bsdsocket.rs b/crates/volamos-core/src/bsdsocket.rs index a473a82..37b4c01 100644 --- a/crates/volamos-core/src/bsdsocket.rs +++ b/crates/volamos-core/src/bsdsocket.rs @@ -1,237 +1,40 @@ -//! `bsdsocket.library`: real TCP/UDP networking via a **host-socket -//! passthrough** -- guest `socket()`/`connect()`/`send()`/`recv()`/... -//! calls translate directly onto real host OS sockets (the `socket2` -//! crate), not an embedded guest-visible TCP/IP stack. See -//! `crates/dispatch.rs`'s [`crate::dispatch::Runtime::enable_bsdsocket`] -//! and the originating design research (GitHub issue) for the full -//! rationale; the short version: Copperline (the cycle-exact Amiga -//! emulator this project uses for ground-truth testing) has a real, -//! conformance-tested `bsdsocket.library` implementation with exactly -//! two transports -- an embedded `smoltcp` stack (needed only because -//! *it* requires cycle-exact determinism) and a host-socket passthrough -//! backend (`transport = "host"`, its "Amiberry-style" option). volamos -//! has no determinism constraint, so the host-socket shape -- not the -//! embedded-stack one -- is the directly applicable model, and this -//! module follows Copperline's own host-side primitives closely (real -//! non-blocking `socket2::Socket`s, a plain handle table, the same -//! `sock_open`/`sock_connect`/`sock_send`/`sock_recv`/... shape) minus -//! the WASM linear-memory-copy boundary Copperline's plugin architecture -//! needs and this runtime doesn't (LVO handlers already have direct -//! `&mut dyn AddressSpace` access). +//! `bsdsocket.library` maps guest socket calls to host TCP/UDP sockets +//! using `socket2`; it does not provide a guest-side network stack. //! -//! # Opt-in, not always-on +//! # Registration and scope //! -//! Unlike `locale.library`/`intuition.library`/`mathffp.library` (real, -//! always-registered libraries this runtime treats as ROM-resident, -//! matching real KS/WB 3.1), `bsdsocket.library` is **not** part of this -//! project's KS/WB 3.1 baseline -- on real hardware it only exists if a -//! third-party TCP/IP stack (AmiTCP/Miami/Genesis/Roadshow) is installed, -//! and giving guest code real host network access is a meaningfully -//! different trust boundary than local filesystem access (which this -//! runtime already gates behind explicit `-V`/`-a` opt-in). So this -//! library is only registered when [`crate::dispatch::Runtime:: -//! enable_bsdsocket`] is called explicitly (wired to a CLI flag), never -//! unconditionally from [`crate::dispatch::Runtime::new`] -- without -//! that opt-in, `OpenLibrary("bsdsocket.library", 0)` behaves exactly -//! like it does today (not found, matching a real bare KS/WB 3.1 -//! system with no TCP stack installed). +//! The library is registered only when +//! [`crate::dispatch::Runtime::enable_bsdsocket`] is enabled, keeping +//! host network access opt-in. Implemented calls cover IPv4 TCP/UDP, +//! socket options, `WaitSelect`, socket events, DNS, and service/protocol +//! lookups. `ObtainSocket` and its release variants return +//! `EOPNOTSUPP`; network database lookups return `NULL`. Other +//! unimplemented LVOs remain unknown calls. //! -//! # Scope: a first, real slice, not the whole LVO surface +//! # Socket behavior //! -//! Implements `socket`/`bind`/`listen`/`accept`/`connect`/`send`/`recv`/ -//! `sendto`/`recvfrom`/`shutdown`/`getsockname`/`getpeername`/ -//! `CloseSocket`/`getdtablesize`/`Errno`/`SetErrnoPtr`/`Inet_NtoA`/ -//! `inet_addr`/`gethostbyname`/`IoctlSocket`/`setsockopt`/`getsockopt`/ -//! `SocketBaseTagList`/`WaitSelect`/`Dup2Socket`/`vsyslog`/ -//! `GetSocketEvents`/`SetSocketSignals`/`gethostbyaddr`/`getservbyname`/ -//! `getservbyport`/`getprotobyname`/`getprotobynumber`/`gethostname`/ -//! `gethostid`/`sendmsg`/`recvmsg`/`Inet_LnaOf`/`Inet_NetOf`/ -//! `Inet_MakeAddr`/`inet_network` -- real outbound and inbound TCP, plus UDP, real error -//! reporting through the documented `Errno()`/`SetErrnoPtr()` mechanism, -//! real forward *and* reverse DNS lookups, and real services/protocols -//! database lookups, all via the host's own resolver/libc (see "DNS: a -//! real, blocking host lookup" below), the -//! socket option set a real conformance suite (`bsdsocktest`'s own -//! `sockopt` test category) actually exercises: `SO_REUSEADDR`/ -//! `SO_KEEPALIVE`/`SO_LINGER`/`SO_SNDBUF`/`SO_RCVBUF`/`SO_SNDTIMEO`/ -//! `SO_RCVTIMEO`/`SO_ERROR`/`SO_TYPE`/`TCP_NODELAY` -- all real -//! `socket2` calls against the host socket, not roundtrip-only storage -//! (`SO_ERROR` in particular has real BSD "read consumes the pending -//! error" semantics, via `socket2`'s own `take_error`), and real -//! `select()`-shaped multiplexing via `WaitSelect` (Unix-only -- see -//! "WaitSelect: real poll(2), not a busy-loop" below). -//! `ObtainSocket`/`ReleaseSocket`/`ReleaseCopyOfSocket`/`getnetbyname`/ -//! `getnetbyaddr` are all registered (so a caller that unconditionally -//! tries any of them doesn't crash the whole run on an unhandled-call -//! trap) but never back a real result: `ObtainSocket`/`ReleaseSocket`/ -//! `ReleaseCopyOfSocket` always honestly fail with `EOPNOTSUPP` (see -//! [`release_socket_handler`]'s doc comment for why a real cross-process -//! handoff isn't possible here); `getnetbyname`/`getnetbyaddr` always -//! return `NULL` (see [`getnetbyname_handler`]'s doc comment for why the -//! real `/etc/networks` database isn't worth wiring up). `IoctlSocket` -//! implements `FIONBIO` (see "Blocking by default" below) and -//! `FIONREAD` (Unix-only, a real `ioctl(2)` on the underlying host -//! socket -- no portable `socket2` equivalent) -- `FIOASYNC` is still -//! genuinely unimplemented (calling it traps as an ordinary -//! unknown-call, same as any other library's unimplemented LVO in this -//! codebase -- see [`crate::lvos::bsdsocket`]'s module docs for why this -//! table only lists what's implemented, not the full ABI; no real -//! conformance test needs it). +//! Sockets are blocking by default. `IoctlSocket(FIONBIO)` opts into +//! non-blocking mode. `WaitSelect` uses `poll(2)` on Unix and returns +//! `EOPNOTSUPP` elsewhere. Because guest execution is single-tasked, +//! its signal mask is checked before polling; no guest signal can arrive +//! while the host call is blocked. //! -//! # Blocking by default +//! `SO_EVENTMASK` arms per-socket `FD_*` events, and +//! `GetSocketEvents` drains detected events. Event-armed sockets are +//! polled alongside the caller's `WaitSelect` descriptors. `FD_CONNECT` +//! detection is omitted. //! -//! Every socket [`socket_handler`]/[`accept_handler`] hand back starts -//! in the OS default **blocking** mode -- the real BSD/AmigaOS default -//! -- not forced non-blocking. This module's first slice got this -//! backwards (every socket was unconditionally -//! `set_nonblocking(true)`'d, with no way to opt out at all): found -//! running a real, unmodified consumer (MicroPython's Amiga port, -//! `ports/amiga/modsocket.c`), whose own `connect()` wrapper has no -//! `EINPROGRESS` handling whatsoever -- it assumes a synchronous -//! `connect()`, and only ever asks for non-blocking mode explicitly via -//! `IoctlSocket(fd, FIONBIO, ...)` (gated behind Python's -//! `settimeout()`/`setblocking()`, which a caller that never touches -//! either never triggers). Forcing every socket non-blocking regardless -//! made every simple, default-mode socket program's very first -//! `connect()` fail with a spurious `EINPROGRESS` it had no way to -//! recover from. [`ioctl_socket_handler`] implements the one `IoctlSocket` -//! command (`FIONBIO`) real socket code actually needs to opt into -//! non-blocking mode explicitly; a blocking `connect`/`accept`/`send`/ -//! `recv` on a socket that hasn't asked for that just blocks the host -//! thread until the real I/O completes -- the same "trust the host, -//! real blocking calls are fine" posture `gethostbyname` already uses, -//! not a shortcut. +//! # Errors and lookups //! -//! # `WaitSelect`: real `poll(2)`, not a busy-loop +//! `Errno()` uses fixed BSD error numbers translated from common +//! `std::io::ErrorKind` values, rather than host-specific errno values. +//! DNS and service/protocol lookups use host facilities and return IPv4 +//! addresses only. Lookup failures use the library `Errno()` channel. +//! Guest `hostent` data remains valid until the next lookup, matching the +//! reusable result buffer behavior. //! -//! [`wait_select_handler`] translates a real AmigaOS `WaitSelect` call -//! (BSD `select()` plus an Exec signal mask -- a real Roadshow Autodoc, -//! see that function's own doc for the exact contract) directly onto a -//! real `libc::poll(2)` call against the underlying host file -//! descriptors (`socket2::Socket::as_raw_fd`) -- Unix only (`#[cfg(unix)]`; -//! this runtime's own Windows support is unverified at runtime already, -//! see `docs/plan.md`'s notes, so `WaitSelect` on Windows returns -//! `EOPNOTSUPP` rather than a guess). This is the same "trust the host" -//! posture every other blocking call in this module already takes -//! (`connect`, `gethostbyname`, ...): the host kernel's own `poll(2)` -//! already solves "is this fd ready" correctly for every socket state -//! this backend can produce (a listening socket's `POLLIN` meaning "a -//! connection is pending `accept()`", a connecting socket's `POLLOUT` -//! meaning "the handshake finished", ...), so there's no reason to -//! reimplement any of that by hand. -//! -//! **The `signals` parameter, and why this runtime doesn't need to poll -//! for it mid-wait**: real `WaitSelect` also races the file-descriptor -//! wait against an Exec signal mask, waking on whichever happens first. -//! Because this runtime is single-tasking and non-preemptive -- no other -//! guest code can run concurrently to `Signal()` the waiting task while -//! `WaitSelect` is blocked inside a single host `poll(2)` call -- a -//! signal can only ever already be pending *before* the call starts, -//! never arrive *during* it. So [`wait_select_handler`] checks -//! `tc_SigRecvd` against the requested mask exactly once, up front: -//! matching real semantics either way ("no socket ready" + "a signal was -//! already pending" `=>` return `0` without ever calling `poll(2)` at -//! all) but skipping the "wake immediately if a signal arrives mid-wait" -//! case, since that case is structurally unreachable in this runtime's -//! own task model -- not a missing feature, just a state this -//! architecture can't produce. `crate::exectask::TC_SIGRECVD` is the -//! same field `SetSignal`/`Wait`/`Signal` already maintain. -//! -//! # `SO_EVENTMASK`/`GetSocketEvents`: real per-socket event detection -//! -//! `SO_EVENTMASK` ([`setsockopt_handler`]/[`getsockopt_handler`]) arms a -//! set of `FD_*` bits (`FD_READ`/`FD_WRITE`/`FD_ACCEPT`/`FD_CLOSE`/ -//! `FD_ERROR` -- real `` values) on one socket; -//! `SocketBaseTagList`'s `SBTC_SIGEVENTMASK` tag arms an Exec signal bit -//! to raise whenever any armed event fires on any socket; -//! `GetSocketEvents` drains one pending `(fd, bits)` pair at a time, -//! round-robin across sockets that have one. There is no dedicated -//! "check for events" call in the real API -- real programs discover -//! events by calling `WaitSelect` (often with every `fd_set` `NULL` and -//! only the signal armed, since the events themselves aren't in any -//! `fd_set`), so [`wait_select_poll`] is where detection actually -//! happens: every event-armed socket rides along in the very same real -//! `poll(2)` call as the caller's own explicit `fd_set` interests -//! (a *single* `poll(2)` covers both, so a real event unblocks a -//! `WaitSelect(0, NULL, NULL, NULL, &tv, &sigmask)` call early exactly -//! like real hardware) -- see that function's own doc comment for the -//! `poll(2)`-revents-to-`FD_*`-bits classification, including how a -//! listening socket's `POLLIN` becomes `FD_ACCEPT` instead of `FD_READ`, -//! and how `FD_CLOSE` is disambiguated from real pending data via a -//! zero-consuming `peek`. Deliberately does not attempt `FD_CONNECT` -//! detection (see that function's doc for why treating an already- -//! writable connected socket as "connect just completed" would misfire -//! on every call) -- `bsdsocktest`'s own `FD_CONNECT` test tolerates -//! this, since a synchronous/fast loopback `connect()` returning `0` -//! directly is one of its explicitly documented-acceptable outcomes. -//! -//! # Errno: real BSD numbering, not the host's own -//! -//! `Errno()`/`SetErrnoPtr()` report the fixed BSD `errno` numbering -//! `bsdsocket.library` itself documents (`` from a real -//! Roadshow NDK) -- not whatever the *host* OS's own errno numbering -//! happens to be (which differs across macOS/Linux/Windows). [`translate_errno`] -//! maps a [`std::io::Error`] to this fixed numbering via -//! [`std::io::ErrorKind`] (the common socket-relevant cases only, same -//! set Copperline's own translator falls back to when a raw host errno -//! doesn't have a direct mapping) -- not by inspecting the host's raw OS -//! errno at all, a deliberate simplification versus Copperline's more -//! thorough platform-specific table; expand this if a real corpus binary -//! needs a specific errno this doesn't yet produce. -//! -//! # DNS: a real, blocking host lookup -//! -//! `gethostbyname` resolves via `(name, 0u16).to_socket_addrs()` -- -//! `std::net`'s standard, portable way to invoke the host OS's own -//! resolver (`getaddrinfo` under the hood), the same "trust the host" spirit -//! as everything else in this module. Unlike Copperline's own -//! `hostsocket-plugin` (which needs a `resolve_start`/`resolve_poll` -//! background-thread-plus-non-blocking-poll dance specifically because a -//! WASM plugin can't block its host's emulation thread), this runtime has -//! no such constraint -- a library call handler already runs synchronously -//! on the guest's own call boundary, so a plain blocking call here is -//! consistent with how every other host I/O in this codebase already -//! works (`Open`, `Seek`, ...), not a shortcut. -//! -//! Only `AF_INET` (IPv4) results are surfaced, matching this module's -//! `AF_INET`-only scope elsewhere; a name that resolves to IPv6-only -//! addresses reports as not found. Real `` documents lookup -//! failures via a separate `extern int h_errno` global "left" by -//! `gethostbyname`/`gethostbyaddr` -- but no LVO in the real -//! `bsdsocket_lib.fd` ever sets one via a registered pointer the way -//! `SetErrnoPtr` does for plain `errno`, meaning on real AmigaOS this -//! value is read back through the *same* `Errno()` channel this module -//! already implements (a real, if slightly surprising, historical -//! AmigaOS quirk, not a simplification here) -- so a failed -//! `gethostbyname` reports one of ``'s own `HOST_NOT_FOUND`/ -//! `TRY_AGAIN`/`NO_DATA` codes through [`BsdSocketState::set_errno`], -//! exactly like every other failing call in this module. -//! -//! The returned `struct hostent` (real Roadshow `` layout: five -//! 4-byte fields, `h_name`/`h_aliases`/`h_addrtype`/`h_length`/ -//! `h_addr_list`, 20 bytes) and everything it points to live on the -//! guest heap and are rebuilt fresh on every call -- real -//! `gethostbyname`'s own documented contract is a reused, overwritten- -//! by-the-next-call static buffer, not a caller-freed allocation (there -//! is no `FreeHostent`-style LVO in the real ABI), so -//! [`BsdSocketState::hostent_allocs`] frees the *previous* call's -//! blocks at the start of the next one rather than ever handing -//! ownership to the guest. -//! -//! # SnoopDos-style logging: `connect`/`accept` set `call_detail` -//! -//! [`connect_handler`]/[`accept_handler`] set [`HandlerContext:: -//! call_detail`] on every *meaningful* outcome (a completed or -//! in-progress outbound connection, an accepted inbound one, or a real -//! failure), printed by the CLI's `-s`/`--snoop` flag exactly like every -//! other resource-opening call already does (`OpenLibrary`/`Open`, see -//! [`crate::dispatch::open_library_common`]'s doc) -- no separate -//! logging mechanism, just this module joining the existing convention. -//! `accept`'s `EAGAIN` ("nothing waiting yet") is deliberately *not* -//! logged: it's the overwhelmingly common outcome of a guest's own -//! accept-loop polling, and logging it would be noise, not a real event -//! -- the same "meaningful events only" posture `--snoop`'s own module -//! doc already promises. +//! `connect` and `accept` outcomes populate `call_detail` for +//! `--snoop`; routine `EAGAIN` results from accept polling are omitted. use std::collections::HashMap; use std::io::{Read, Write}; diff --git a/crates/volamos-core/src/intuition.rs b/crates/volamos-core/src/intuition.rs index 97ac3ba..2afeb5e 100644 --- a/crates/volamos-core/src/intuition.rs +++ b/crates/volamos-core/src/intuition.rs @@ -1,105 +1,14 @@ -//! `intuition.library`: a thin stub -- `DisplayAlert`, `AutoRequest`, -//! `EasyRequestArgs`, `CurrentTime`, matching vamos's own scope for this -//! library ("just enough that a console tool calling a stray Intuition -//! function doesn't crash. No real windowing/GUI"), added closing a gap -//! found comparing volamos's coverage against vamos's own -//! (`docs/plan.md`'s dated entry). This runtime has no display and never -//! renders anything -- every function here does the least a headless -//! runtime honestly can, not a real GUI implementation. +//! Headless `intuition.library` support provides inspectable screen and +//! window structures, but no rendering or user input. A default public +//! screen is created lazily; windows attach to it, and custom screens +//! are unsupported. Requesters return documented defaults +//! (`AutoRequest`: `TRUE`; `EasyRequestArgs`: `0`). `DisplayAlert` +//! follows `exec.library` alert semantics, and `CurrentTime` reads the +//! host clock. //! -//! # The default screen: real struct, no pixels -//! -//! Unlike the rest of this module, there *is* now a `Screen`/`Window` -//! model, deliberately minimal: a single, always-available default -//! public screen (`"Workbench"`, 640x200x1 -- a plain NTSC lores -//! non-interlace mode, the same one a stock KS/WB 3.1 boot without a -//! Workbench disk comes up in), lazily allocated on first use as a real -//! `struct Screen` in guest memory ([`ensure_default_screen`]) with -//! honest `Width`/`Height`/`Flags`/embedded `ViewPort`/`RastPort`/ -//! `BitMap` fields -- [`lock_pub_screen_handler`]/ -//! [`get_screen_data_handler`] read real data off it, not canned -//! answers. There is deliberately no support for opening *additional* -//! or *custom* screens (`OpenScreen` stays unregistered/unhandled) -- -//! see `docs/plan.md`'s dated entry for the reasoning: almost every -//! guest program that touches Intuition just wants to query the one -//! screen that's always there, not create its own. -//! -//! `OpenWindow`/`CloseWindow` extend the same idea to windows: a guest -//! that opens a window gets back a real `struct Window` -- correctly -//! wired `WScreen`/`RPort`/`UserPort` pointers, so any code that merely -//! *holds onto* the handle (passes it to another library call, reads -//! its geometry, polls its `UserPort` for messages) sees consistent, -//! honest data -- but nothing is ever drawn into its `RastPort`, and -//! its `UserPort` never receives an `IntuiMessage` (there's no user -//! input to report -- same limitation as `AutoRequest`/ -//! `EasyRequestArgs` below). A program that actually expects to *see* -//! its window, or that blocks forever waiting for an `IDCMP` message -//! that will never arrive, won't work here -- an inherent limit of a -//! headless runtime, not something worth working around with fake -//! input. -//! -//! Struct layouts (`SCR_*`/`WIN_*`/`VP_*`/`RP_*`/`BM_*` byte-offset -//! consts below) are hand-computed from the NDK 3.2 headers -//! (`intuition/screens.h`, `intuition/intuition.h`, `graphics/view.h`, -//! `graphics/rastport.h`, `graphics/gfx.h`) using m68k's natural -//! (word, not long) struct alignment -- same provenance discipline as -//! `doslock.rs`'s `FL_*`/`FIB_*` consts. Two deliberate approximations: -//! `struct Layer_Info` (embedded in `Screen`) is reserved as an opaque -//! zeroed blob sized generously rather than field-accurate, since -//! nothing in this runtime (there's no `layers.library`) ever reads its -//! fields; and `struct Window`'s private tail past `MoreFlags` (per the -//! header's own "Intuition Private, DO NOT USE" comment) isn't -//! reproduced at all. -//! -//! # `DisplayAlert`: reuses `exec.library`'s own `Alert` handling -//! -//! Real `DisplayAlert` is Intuition's own presentation layer for the -//! same Guru-Meditation-style alert `exec.library`'s `Alert` (see -//! `crate::exectask::alert_handler`) triggers when Intuition is -//! available to draw it -- same `AT_DeadEnd` semantics (a dead-end -//! alert means the guest declared system integrity can't be -//! guaranteed), just with a custom message string and height instead of -//! a numeric code. This handler mirrors `alert_handler` exactly: always -//! logs to stderr unconditionally (a real alert is never silent on real -//! hardware), dead-end fails loudly via -//! [`DispatchError::HandlerFailed`], recoverable returns `TRUE` -//! (`D0`) as if the user dismissed it instantly (there's no display to -//! wait on input from). -//! -//! # `AutoRequest`/`EasyRequestArgs`: no display, no real choice to report -//! -//! Both real functions show a requester and report *which button the -//! user pressed*. With no display, there's no user input to honestly -//! report -- these return a fixed default (`AutoRequest`: `TRUE`, -//! matching its positive/default gadget; `EasyRequestArgs`: `0`, its -//! documented "leftmost gadget" result) rather than trying to guess -//! which choice a real user would have made. A caller that actually -//! branches on the result differently per button will see the wrong -//! branch -- an inherent limitation of a headless stub, not something -//! this runtime can resolve without a real display. -//! -//! # `GetScreenData`/`LockPubScreen`/`UnlockPubScreen`: the default -//! screen, honestly -//! -//! `GetScreenData` now copies real `struct Screen` fields (from the -//! default screen -- see "The default screen" above) into the caller's -//! buffer, `min(size, sizeof(struct Screen))` bytes of it, and returns -//! `TRUE`. `LockPubScreen` returns the default screen's address for a -//! `NULL` name or a name matching it (case-sensitively -- see -//! [`lock_pub_screen_handler`]); any other name honestly fails with -//! `NULL`, since no other public screen actually exists here. -//! `UnlockPubScreen` is a no-op (this runtime doesn't track lock -//! counts; there's only ever the one screen and it never actually -//! closes). -//! -//! # `CurrentTime`: the one genuinely real function here -//! -//! Unlike the requester functions, `CurrentTime` has an honest, correct -//! answer even headlessly: the real host wall-clock time. Reuses -//! [`crate::exectask::host_time_secs_micro`], the same AmigaOS-epoch -//! (1978-01-01) seconds-plus-microseconds source -//! `timer.device`'s`GetSysTime` already uses -- `CurrentTime`'s own -//! Autodoc documents the identical epoch/units. +//! Struct offsets follow NDK 3.2 headers and m68k word alignment. +//! `Layer_Info` is reserved as an opaque zeroed region, and the private +//! `Window` tail after `MoreFlags` is omitted. use crate::cpu::{AddressRegister, Cpu, DataRegister}; use crate::dispatch::{DispatchError, HandlerContext, LibraryTable}; @@ -136,8 +45,7 @@ const SCR_VIEWPORT: u32 = 44; const SCR_RASTPORT: u32 = 84; /// Byte offset of the embedded `struct BitMap` within `struct Screen`. const SCR_BITMAP: u32 = 184; -/// Byte offset of the (opaque, zeroed) `struct Layer_Info` within -/// `struct Screen` -- reserved space only, see the module docs. +/// Byte offset of the reserved, zeroed `struct Layer_Info` region. const SCR_LAYERINFO: u32 = 224; const SCR_LAYERINFO_SIZE: u32 = 88; const SCR_FIRSTGADGET: u32 = 312; @@ -177,8 +85,7 @@ const BM_DEPTH: u32 = 5; // ---- struct Window (intuition/intuition.h) ----------------------------- -/// `sizeof(struct Window)` up to (and including) `MoreFlags` -- the -/// header's own documented public boundary, see the module docs. +/// `sizeof(struct Window)` through the public `MoreFlags` field. const WINDOW_SIZE: u32 = 136; const WIN_NEXTWINDOW: u32 = 0; const WIN_LEFTEDGE: u32 = 4; @@ -248,10 +155,7 @@ const NW_SCREEN: u32 = 30; /// `BOOL` `TRUE`, per ``. const DTRUE: u32 = 1; -/// Host-side `intuition.library` state: just the default screen's guest -/// address, lazily filled in by [`ensure_default_screen`] on first use -/// (mirroring how `OpenLibrary`'s fake libraries are created lazily on -/// first `OpenLibrary`, not eagerly at boot). See the module docs. +/// Host-side state: the lazily allocated default screen address. pub struct IntuitionState { default_screen: Option, } @@ -270,10 +174,8 @@ impl Default for IntuitionState { } } -/// Returns the guest address of the always-available default public -/// screen (`"Workbench"`, 640x200x1), allocating and initializing it on -/// first call. See the module docs for what is and isn't honestly -/// modeled. +/// Returns the default public screen (`"Workbench"`, 640x200x1), +/// allocating and initializing it on first use. fn ensure_default_screen( state: &mut IntuitionState, heap: &mut GuestHeap, @@ -358,9 +260,8 @@ fn ensure_default_screen( const AT_DEAD_END: u32 = 0x8000_0000; /// `DisplayAlert` (`D0` = `alertNumber`, `A0` = `string` `CString*`, -/// `D1` = `height`, ignored -- a real display-layout hint this runtime -/// has no display to apply). `D0` = `TRUE`/`FALSE` (`BOOL`) -- see the -/// module docs. +/// `D1` = `height`, ignored because this runtime has no display. +/// `D0` = `TRUE` for recoverable alerts; dead-end alerts fail dispatch. fn display_alert_handler(ctx: &mut HandlerContext<'_, C>) -> Result<(), DispatchError> { let alert_num = ctx.cpu.data_register(DataRegister(0)); let string_ptr = ctx.cpu.address_register(AddressRegister(0)); @@ -392,9 +293,8 @@ fn display_alert_handler(ctx: &mut HandlerContext<'_, C>) -> Result<(), } /// `AutoRequest` (`A0` = window, `A1`/`A2`/`A3` = body/positive/negative -/// `IntuiText*`, `D0`-`D3` = flags/width/height -- all ignored, no -/// display to show any of it on). `D0` = `TRUE` unconditionally -- see -/// the module docs. +/// `IntuiText*`, `D0`-`D3` = flags/width/height, all ignored. `D0` is +/// always `TRUE` because this runtime has no display or user input. fn auto_request_handler(ctx: &mut HandlerContext<'_, C>) -> Result<(), DispatchError> { ctx.cpu.set_data_register(DataRegister(0), 1); Ok(()) @@ -402,8 +302,7 @@ fn auto_request_handler(ctx: &mut HandlerContext<'_, C>) -> Result<(), D /// `EasyRequestArgs` (`A0` = window, `A1` = `struct EasyStruct*`, `A2` = /// `IDCMP` pointer, `A3` = args -- all ignored). `D0` = `0` (the -/// documented "leftmost gadget" result) unconditionally -- see the -/// module docs. +/// documented "leftmost gadget" result) unconditionally. fn easy_request_args_handler(ctx: &mut HandlerContext<'_, C>) -> Result<(), DispatchError> { ctx.cpu.set_data_register(DataRegister(0), 0); Ok(()) @@ -412,8 +311,8 @@ fn easy_request_args_handler(ctx: &mut HandlerContext<'_, C>) -> Result< /// `GetScreenData` (`A0` = buffer, `D0` = buffer size, `D1` = screen /// type, `A1` = screen -- `D1`/`A1` ignored, this runtime has only the /// one default screen regardless of requested type). Copies -/// `min(size, sizeof(struct Screen))` bytes of the real default screen -/// into the buffer; `D0` = `TRUE`. See the module docs. +/// `min(size, sizeof(struct Screen))` bytes of the default screen into +/// the buffer; `D0` = `TRUE`. fn get_screen_data_handler(ctx: &mut HandlerContext<'_, C>) -> Result<(), DispatchError> { let buffer = ctx.cpu.address_register(AddressRegister(0)); let size = ctx.cpu.data_register(DataRegister(0)); @@ -429,8 +328,7 @@ fn get_screen_data_handler(ctx: &mut HandlerContext<'_, C>) -> Result<() /// `LockPubScreen` (`A0` = `CONST_STRPTR` screen name, or `NULL` for /// the default public screen). Returns the default screen's address -/// for `NULL` or a name matching it; any other name returns `NULL` -- -/// see the module docs. +/// for `NULL` or the exact name `"Workbench"`; other names return `NULL`. fn lock_pub_screen_handler(ctx: &mut HandlerContext<'_, C>) -> Result<(), DispatchError> { let name_ptr = ctx.cpu.address_register(AddressRegister(0)); let matches_default = if name_ptr == 0 { @@ -448,8 +346,8 @@ fn lock_pub_screen_handler(ctx: &mut HandlerContext<'_, C>) -> Result<() Ok(()) } -/// `UnlockPubScreen` (`A0` = name, ignored; `A1` = screen). No-op -- -/// see the module docs. +/// `UnlockPubScreen` (`A0` = name, ignored; `A1` = screen). No-op; +/// this runtime does not track screen locks. fn unlock_pub_screen_handler( _ctx: &mut HandlerContext<'_, C>, ) -> Result<(), DispatchError> { @@ -463,9 +361,8 @@ fn unlock_pub_screen_handler( /// window's screen's `BitMap`; `UserPort`/`WindowPort` are a real /// no-op `struct MsgPort` (see [`crate::execlist`]'s `GetMsg`/ /// `WaitPort` -- it will simply never receive an `IntuiMessage`, there -/// being no user input to report). `D0` = the new `Window*`, or `0` on -/// guest-heap exhaustion (real `OpenWindow`'s own documented failure -/// return). See the module docs. +/// being no user input to report). `D0` = the new `Window*`, or `0` if +/// guest-heap allocation fails. fn open_window_handler(ctx: &mut HandlerContext<'_, C>) -> Result<(), DispatchError> { let new_window = ctx.cpu.address_register(AddressRegister(0)); @@ -600,7 +497,7 @@ fn close_window_handler(ctx: &mut HandlerContext<'_, C>) -> Result<(), D } /// `CurrentTime` (`A0` = `ULONG*` seconds out-param, `A1` = `ULONG*` -/// micros out-param). No return value. See the module docs. +/// micros out-param). No return value; both outputs use the Amiga epoch. fn current_time_handler(ctx: &mut HandlerContext<'_, C>) -> Result<(), DispatchError> { let seconds_ptr = ctx.cpu.address_register(AddressRegister(0)); let micros_ptr = ctx.cpu.address_register(AddressRegister(1));