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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 17 additions & 7 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,17 @@ hashes are given where a change is easiest to understand by reading the commit.

## November 2026

### One key for the audio chip, one for where it plays

- New `[machine] audio = true|false` (`--audio`): the Indy and Indigo2 have HAL2
unless it is `false`, and the Tezro has its ICE1724 only if it is `true`.
- New `[audio] output = "default" | "alsa" | "pulseaudio" | "none" | { file = "out.wav" }`
(`--audio-output`) says where a present card plays, on every machine.
- Compatibility: `no_audio`, `[audio] host`, `[audio] file`, `[audio] prebuf_ms`,
`[tezro] audio`, `--noaudio` and `--audio-host` still work, print a line
naming their replacement, and leave every guest's hardware as it was. A
misspelt key in `[audio]` is now an error.

### Serial logs append on every machine

- New `serial_log_overwrite` (`--serial-log-overwrite`) starts `serial_log`, `serial_log_a` and `serial_log_b` over each run instead of adding to them.
Expand Down Expand Up @@ -45,14 +56,13 @@ hashes are given where a change is easiest to understand by reading the commit.

### Tezro: optional PCI audio

- `[tezro] audio = true` adds an ICE1724 audio controller behind the IO9's PCI
bridge. It plays through `[audio]`, or silently with `no_audio`, and is off
by default. See the [Tezro guide](docs/tezro-ip53.md).
- `[machine] audio = true` adds an ICE1724 audio controller behind the IO9's PCI
bridge. It plays through `[audio]` and is off by default. See the [Tezro guide](docs/tezro-ip53.md).

### Host audio

- `[audio] file = "out.wav"` plays every machine's sound into a WAV file in
real time instead of a sound card.
- `[audio] output = { file = "out.wav" }` plays every machine's sound into a WAV
file in real time instead of a sound card.
- The monitor's `audio status` reports every audio card (HAL2, MACE audio, RAD1
on the Octane and Onyx2, the Tezro's ICE1724) in one layout: per output the
frames moved and the guest rate, host underruns, frames the host dropped and
Expand All @@ -64,8 +74,8 @@ hashes are given where a change is easiest to understand by reading the commit.
- Compatibility: the developer environment variables `IRIS_HAL2_CAPTURE`,
`IRIS_O2_AUDIO_CAPTURE`, `IRIS_OCTANE_AUDIO_CAPTURE`, `IRIS_OCTANE_AUDIO_WAV`
and `IRIS_O2_AUDIO_DUMP` are gone. Use
`IRIS_AUDIO_DUMP` for the card's own samples, or `[audio] file` for a WAV of
what the host plays. The O2's dump keeps its layout.
`IRIS_AUDIO_DUMP` for the card's own samples, or `[audio] output = { file }`
for a WAV of what the host plays. The O2's dump keeps its layout.

### Every machine has a built-in PROM

Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ nokhwa = { version = "0.10", features = ["input-avfoundation"] }

[target.'cfg(target_os = "linux")'.dependencies]
# `pulseaudio`: cpal's own PulseAudio client (pure Rust, links nothing), for
# `[audio] host = "pulseaudio"`. HAL2 picks the host itself, so the default
# `[audio] output = "pulseaudio"`. The cards pick the host through iris, so the default
# stays ALSA even though this feature makes cpal's default_host() prefer it.
cpal = { version = "0.18", features = ["pulseaudio"] }
nokhwa = { version = "0.10" }
Expand Down
15 changes: 7 additions & 8 deletions FEATURES.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,6 @@ the first `[section]`, because TOML tables continue until the next header.
| `banks` | `[128,128,0,0]` | RAM bank sizes in MB, up to eight entries (every machine today has four; the rest must be 0). On the O2 each entry is a DIMM pair's kit (32, 64, 128 or 256), largest first. On the Octane each entry is a pair of DIMMs, 64, 128, 256, 512, 1024 or 2048, filled from bank 0, 8192 in all. See profile constraints below. |
| `scale` | `1` | Standalone window scale: 1–4. GUI uses its separate vm_scale. |
| `headless` | `false` | Disable the standalone window and graphics; the O2 then boots on its serial console. Audio is independent. |
| `no_audio` | `false` | Disable audio emulation (HAL2, MACE or RAD). |
| `gdb_port` | `unset` | Start the GDB TCP stub; debugging is unavailable in lightning builds. |
| `monitor_port` | `8888 when unset` | Monitor listener on loopback. |
| `serial_port_a` | `8880 when unset` | SCC channel A listener (tty2); the O2's first serial port, its console when headless. |
Expand Down Expand Up @@ -184,6 +183,7 @@ the first `[section]`, because TOML tables continue until the next header.
| `machine.cpu` | the machine's first | A family (`r4400`, `r5000`, `r10000`, `r12000`, `r14000`) or a part (`r14000_600`); independent of build features. Indy and `indigo2_ip22`: r4400, r5000; `indigo2_ip28`: r10000; O2: r5000, r10000, r12000; Octane: r10000, r12000, r14000; Origin 200, Origin 2000 and Onyx2: r10000 (`r10000_180`), r12000 (`r12000_270`); Fuel: r14000 (`r14000_500`); Tezro: r16000 (a bare `r16000` is `r16000_700`; `r16000_800`, `r16000_900` and `r16000_1000` are the other parts it ships with); Origin 3200: r12000 (`r12000_400`, with 8 MiB of secondary cache). The first listed is the default. |
| `machine.cpus` | `1`; `4` on the Tezro | Processors fitted. 2 on the Octane; see [its guide](docs/octane-ip30-multiprocessor.md). 1, 2 or 4 on the Tezro, each run on its own host thread; one selects the one-processor board with one PIC, two or four the board with two PICs. |
| `machine.emu_id` | `0` | 0 to 4095; only the Fuel and Tezro take it, and other machines refuse a nonzero one. Mints the machine's system id (`sysinfo -s`, `lmhostid`) in iris's layout. When nonzero it gives the station address `08:00:69:e2:<emu_id>`, which is the Fuel's id. The Tezro's L1 system serial is `LEMU2<emu_id>0`, whose fold is the Tezro's id. 0 is the generic id. See [the Fuel guide](docs/fuel-ip34.md) and [the Tezro guide](docs/tezro-ip53.md). |
| `machine.audio` | `true` on the Indy and Indigo2, `false` on the Tezro | Whether the machine has its audio chip. Only those machines take it; every other one refuses it with an error naming the machine (on the O2, Octane and Onyx2 the card is part of the board: use `audio.output = "none"` to silence it). On the Indy and Indigo2, `false` leaves HAL2 out: the guest sees no audio hardware. On the Tezro, `true` adds an ICE1724 on the IO9's PCI bridge (see the [Tezro guide](docs/tezro-ip53.md)); off, that bus is empty. |

### [origin200], [origin2000] and [onyx2]

Expand Down Expand Up @@ -234,7 +234,6 @@ Tezro-specific settings apply when `machine.profile = "tezro_ip53"`.
| `l1_port` | `8882` | Host TCP port for the L1 console. |
| `l1_log` | unset | Optional serial log path for L1 output when an IOC4 port is selected as console. |
| `serial_port_c`, `serial_port_d` | `8883`, `8884` | Host TCP ports of IOC4 ports 2 and 3; ports 0 and 1 use `serial_port_a` and `serial_port_b`. |
| `audio` | `false` | Add the ICE1724 audio controller on the IO9's PCI bridge. Off, the bridge's bus is empty. On, it plays to the host as `[audio]` sets, or silently with `no_audio`. See the [Tezro guide](docs/tezro-ip53.md). |

The machine runs the 700 MHz R16000 (`r16000_700`, the default), or the 800, 900
or 1000 MHz one (`r16000_800`, `r16000_900`, `r16000_1000`, with 4, 8 and 16 MiB
Expand Down Expand Up @@ -359,9 +358,7 @@ Omit `[nfs]` to disable the share; the default forward list is empty.

| Key | Default | Values and behavior |
|---|---|---|
| `audio.host` | `"default"` | Host audio system: `"default"` (CoreAudio on macOS, WASAPI on Windows, ALSA on Linux), `"alsa"`, or `"pulseaudio"` (Linux: a PulseAudio server or PipeWire's pipewire-pulse, through cpal's own client; ALSA if none answers). `--audio-host` overrides. |
| `audio.prebuf_ms` | `20` | HAL2 prebuffer duration in milliseconds. Not used yet: HAL2 prebuffers 20 ms. |
| `audio.file` | `unset` | Play into this WAV file instead of a sound card: 48 kHz 16-bit stereo, written in real time as the host stream would play it, silence included. For headless runs and for checking what a guest played. HAL2, O2 audio and Octane RAD1. |
| `audio.output` | `"default"` | Where a present audio card plays, on every machine that has one: `"default"` (CoreAudio on macOS, WASAPI on Windows, ALSA on Linux), `"alsa"`, `"pulseaudio"` (Linux: a PulseAudio server or PipeWire's pipewire-pulse, through cpal's own client; ALSA if none answers), `"none"` (the card runs and counts, and no host stream is opened), or `{ file = "out.wav" }` (a WAV file instead of a sound card: 48 kHz 16-bit stereo, written in real time as the host stream would play it, silence included, for headless runs and for checking what a guest played). `--audio-output default\|alsa\|pulseaudio\|none\|file:PATH` overrides. |
| `audio.cpal_buffer_frames` | `host default when unset` | Host buffer size in frames (a period; PulseAudio buffers two). Unset: the host's default, except PulseAudio's (about 2 s), which gets 512. A size the device refuses falls back to the default. |

### [jitv2]
Expand Down Expand Up @@ -590,9 +587,11 @@ iris-gui does not parse this standalone option set; use its named-machine UI.
| `--cdrom5-extra` | Additional ISO images for CD-ROM ID 5 (can be specified multiple times) |
| `--cdrom6-extra` | Additional ISO images for CD-ROM ID 6 (can be specified multiple times) |
| `--2x` | 2× window scaling for HiDPI/4K monitors |
| `--headless` | Run headless: no window, no REX3 graphics (audio unaffected; use --noaudio to disable) |
| `--noaudio` | Disable audio emulation (no HAL2); graphics still works |
| `--audio-host <HOST>` | Host audio system for HAL2: `default`, `alsa` or `pulseaudio` (overrides `[audio] host`) |
| `--headless` | Run headless: no window, no REX3 graphics (audio unaffected; see `--audio` and `--audio-output`) |
| `--audio <BOOL>` | Sets `machine.audio`: whether the machine has its audio chip. `false` leaves HAL2 out of an Indy or Indigo2, `true` adds the Tezro's ICE1724. Applied after `--machine`; an error on any other machine. |
| `--audio-output <OUTPUT>` | Sets `audio.output`: `default`, `alsa`, `pulseaudio`, `none` or `file:PATH`. |
| `--noaudio` | Deprecated: `--audio false` on the Indy and Indigo2, `--audio-output none` on every other machine. |
| `--audio-host <HOST>` | Deprecated: `--audio-output`. |
| `--nfs-dir` | Enable NFS share: path to the directory to export (enables NFS) |
| `--nat-subnet` | NAT subnet in CIDR notation (e.g. 192.168.5.0/24). Gateway gets .1, guest (IRIX) gets .2. Default: 192.168.0.0/24. |
| `--net-mode` | Networking backend: "nat" (default, software gateway), "pcap" (bridge onto a real host interface; requires --features pcap), "stream" (a private segment; needs `network.stream`) or "off". |
Expand Down
19 changes: 9 additions & 10 deletions HELP.md
Original file line number Diff line number Diff line change
Expand Up @@ -617,7 +617,6 @@ banks = [128, 128, 0, 0]

scale = 1 # window scale, kept across guest mode changes; --2x overrides
headless = false # no window, no graphics; the O2 boots on its serial console (audio unaffected)
no_audio = false # no HAL2 / cpal (graphics unaffected)
lock_aspect_ratio = true # false = free resize with letterboxing
# renderer = "gl" # "wgpu": draw the window through wgpu (builds with the wgpu feature)
mouse_scroll_pixels_per_line = 40
Expand Down Expand Up @@ -726,11 +725,9 @@ source = "off" # "off" (default) | "test_pattern" | "black" | "came
standard = "ntsc" # or "pal"
camera_index = 0 # host camera, source = "camera" only

[audio]
# host = "default" # Linux: "alsa" (the default) or "pulseaudio" (PulseAudio or PipeWire)
prebuf_ms = 20
[audio] # where a present audio card plays; [machine] audio = false leaves HAL2 out of an Indy or Indigo2
# output = "default" # "alsa", "pulseaudio" (PulseAudio or PipeWire), "none", or { file = "out.wav" }
# cpal_buffer_frames = 512 # unset: the host's default (PulseAudio: 512)
# file = "out.wav" # play into a WAV file instead of a sound card

# [ultra64] # N64 dev board
# enabled = true
Expand Down Expand Up @@ -787,8 +784,7 @@ or touching the sound device. It exits 0 and prints `iris: FILE: ok (machine)`
if a run from the command line would start, and otherwise exits 1 with one
`Configuration error:` line per problem on stderr; a missing disk image is one,
because a command-line run stops on it, and so is a missing `prom` file you named.
A `Warning:` line is for something a run carries on past, such as a Tezro's audio
card with `no_audio` set. Other options (`--scsi1`, `--cpu` and so on) apply first,
Other options (`--scsi1`, `--cpu` and so on) apply first,
as they do in a run.

It checks that the file parses and has no unknown keys; that the settings agree
Expand Down Expand Up @@ -832,8 +828,11 @@ Storage
Display and audio
--2x 2× window scaling
--headless No window, no REX3 graphics (audio unaffected)
--noaudio Disable HAL2 audio (graphics unaffected)
--audio-host HOST Host audio for HAL2: default, alsa or pulseaudio (Linux)
--audio BOOL Whether the machine has its audio chip: false leaves HAL2 out of
an Indy or Indigo2, true adds the Tezro's ICE1724 ([machine] audio)
--audio-output OUTPUT Where a present card plays: default, alsa, pulseaudio, none or
file:PATH ([audio] output)
(--noaudio and --audio-host are deprecated spellings of the two above)

Networking
--nat-subnet <CIDR> NAT subnet, e.g. 192.168.5.0/24
Expand Down Expand Up @@ -895,7 +894,7 @@ iris --config /opt/iris/my_machine.toml
iris --headless

# Headless with audio also disabled (lightest possible server mode)
iris --headless --noaudio
iris --headless --audio false

# Network-boot from the PROM: then `boot -f bootp()/unix` at the PROM monitor
iris --tftp-dir ./tftpboot
Expand Down
4 changes: 2 additions & 2 deletions TESTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,12 +134,12 @@ A sound card can break anywhere between guest memory and the host's
speakers, and a CI runner has no speakers. Two backends hear what a card
sends to the host:

- **`[audio] file`**: the card's host ring played into a WAV file in real
- **A file output** (`[audio] output = { file = ... }`): the card's host ring played into a WAV file in real
time, silent where the card was late. It needs no sound card. It cannot
see cpal, the operating system's sound server or its mixer.
- **The real device**: the card plays into the host's default output and
the test records it back from a loopback. `IRIS_AUDIO_LOOPBACK` names it
as `<output host>:<capture source>`: the `[audio] host` the card plays
as `<output host>:<capture source>`: the `[audio] output` the card plays
through (`alsa`, `pulseaudio` or `default`) and what hears the default
output. On Linux that is a PulseAudio source, recorded with `parec`. On
Windows it is `default-render`, cpal's WASAPI loopback of the default
Expand Down
2 changes: 1 addition & 1 deletion bench/run/bare.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@

banks = [128, 128, 0, 0]
headless = true
no_audio = true

[machine]
profile = "indy_ip24"
audio = false

[scsi]
2 changes: 1 addition & 1 deletion bench/run/run-local.sh
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ timeout "$TIMEOUT" "$IRIS" \
--config run/bare.toml \
--load-elf "$ELF" \
--test-device \
--headless --noaudio \
--headless --audio false \
"$@" 2>&1 | tee "$LOG"
rc=${PIPESTATUS[0]}

Expand Down
4 changes: 2 additions & 2 deletions bench/run/run-prom.sh
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ cat > build/prom.toml <<'TOML'
# `boot -f dksc(0,2,8)irisbench` reads naturally.
banks = [128, 128, 0, 0]
headless = true
no_audio = true

[machine]
profile = "indy_ip24"
audio = false

[scsi]

Expand All @@ -44,7 +44,7 @@ TOML

rm -f "$SOCK" "$LOG"
"$IRIS" --config build/prom.toml --ci --ci-socket "$SOCK" \
--test-device --headless --noaudio --serial-log "$LOG" --serial-log-overwrite \
--test-device --headless --audio false --serial-log "$LOG" --serial-log-overwrite \
> build/prom-stdout.log 2>&1 &
IRIS_PID=$!
trap 'kill $IRIS_PID 2>/dev/null' EXIT
Expand Down
2 changes: 1 addition & 1 deletion clanker/INDEX.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ Non-graphics devices: MC/MEMCFG and RAM banks, HPC3, VDMA, EEPROMs and NVRAM, PS
| [embedded-prom-compression.md](devices/embedded-prom-compression.md) | measure | Which compression the built-in PROMs use: deflate per PROM against LZMA2, zstd and dictionaries, sizes, decode times and git cost; why deflate won |
| [audio-test-suite.md](devices/audio-test-suite.md) | howto | The audio test suite (`test/audiotest`): rates x channels x widths through the audio library, playaiff and sfplay, checked against what the card played (`IRIS_AUDIO_DUMP`) |
| [boot-chimes.md](devices/boot-chimes.md) | howto | What each machine's PROM plays at power-on (Indy, Indigo2, Octane, O2), and how to check it sample for sample with `IRIS_AUDIO_DUMP` |
| [cpal-alsa-busy-poll.md](devices/cpal-alsa-busy-poll.md) | rule | cpal's ALSA backend can spin a core: `[audio] host = "pulseaudio"` |
| [cpal-alsa-busy-poll.md](devices/cpal-alsa-busy-poll.md) | rule | cpal's ALSA backend can spin a core: `[audio] output = "pulseaudio"` |
| [decode-table-is-host-mapped.md](devices/decode-table-is-host-mapped.md) | rule | The physical decode table is a sparse host mapping over 40 bits: change it only through `fill`, set the default first, compare entries with `entry_eq` |
| [mc-decodes-32-physical-bits.md](devices/mc-decodes-32-physical-bits.md) | rule | Indy/Indigo2/O2 decode 32 physical bits; IRIX PTE software bits reach PA[35:32]; fold with `MipsExecutor::phys_mask`, never decode them |
| [extended-ram-memcfg.md](devices/extended-ram-memcfg.md) | rule | Extended RAM — MEMCFG synthesis for himem banks |
Expand Down
2 changes: 1 addition & 1 deletion clanker/devices/audio-test-suite.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ to a file with `IRIS_AUDIO_DUMP=FILE`, what the card played at the guest's own
rate, before resampling, as records of rate (u32) and pair count (u32), then
the pairs, little-endian. The O2 puts the output number (u8) in front of
every record. It needs no host sound device. `check.py` measures each case
at its records' rate. (A WAV from `[audio] file` works too, at the stream's
at its records' rate. (A WAV from `[audio] output = { file = ... }` works too, at the stream's
rate.)

```sh
Expand Down
6 changes: 3 additions & 3 deletions clanker/devices/boot-chimes.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ the frame count (u32), then that many left, right pairs of little-endian i16.
The O2 has two outputs and puts the output number (u8) in front of each record.
The ICE1724 records the front pair of its mix. Each card records where it makes
its frames: HAL2 before its output gain, RAD1 after its mute words, MACE after
the DAC gain. (`[audio] file` writes a WAV of what the host plays instead.)
the DAC gain. (`[audio] output = { file = ... }` writes a WAV of what the host plays instead.)

What the dump asserts is the guest's work: the samples are what the PROM
computed and handed to the card, in order, at the rate it programmed. A tune
Expand All @@ -40,8 +40,8 @@ the same tune. It does not exercise the host audio device or the resampler.

Requirements:

- The Indy and Indigo2 need audio on: `no_audio` removes HAL2, so nothing plays
and nothing is recorded.
- The Indy and Indigo2 need audio on: `[machine] audio = false` removes HAL2, so
nothing plays and nothing is recorded.
- The IP28 needs `banks = [512, 512, 0, 0]` to chime.
- The Octane run needs the config's SCSI image files to exist. The O2 needs a
flash file (a new one is fine).
Expand Down
4 changes: 2 additions & 2 deletions clanker/devices/cpal-alsa-busy-poll.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# cpal's ALSA backend can spin a core: `[audio] host = "pulseaudio"`
# cpal's ALSA backend can spin a core: `[audio] output = "pulseaudio"`

**Keywords:** hal2,audio,alsa,cpal,pulseaudio,pipewire,busy-poll,xrun,underrun,linux,red led

Expand All @@ -14,7 +14,7 @@ loops, about five million times a second. Seen through PipeWire's ALSA plugin
with the same period and buffer sizes sleeps normally, so it is cpal's loop,
not the driver. Not reproduced yet on bare metal; it may need the VM's timing.

**Workaround.** `[audio] host = "pulseaudio"` (or `--audio-host pulseaudio`):
**Workaround.** `[audio] output = "pulseaudio"` (or `--audio-output pulseaudio`):
cpal's own PulseAudio client (pure Rust, the `pulseaudio` feature) talks to a
PulseAudio server or PipeWire's pipewire-pulse over the socket. The same
stream used 0.01 s of CPU in 5 s. The default stays ALSA until the bug is
Expand Down
2 changes: 0 additions & 2 deletions clanker/irix/irix-install.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,6 @@ in cycle order:

```toml
headless = false
no_audio = false
banks = [128, 128, 0, 0]
# Per-config NVRAM so the 5.3 and 6.5 PROM env don't collide.
nvram = "nvram-irix65.bin"
Expand Down Expand Up @@ -143,7 +142,6 @@ after install:

```toml
headless = false
no_audio = false
banks = [128, 128, 0, 0]
# Per-config NVRAM so the 5.3 and 6.5 PROM env don't collide.
nvram = "nvram-irix53.bin"
Expand Down
Loading
Loading