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
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,14 @@ hashes are given where a change is easiest to understand by reading the commit.
frames moved and the guest rate, host underruns, frames the host dropped and
where the output goes. Underruns now count only while an output runs. It
replaces `hal2 status`.
- `IRIS_AUDIO_DUMP=FILE` records what every audio card plays, at the card's own
rate before resampling, with or without a sound device (a developer
environment variable; the layout is in `FEATURES.md`).
- 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.

### Every machine has a built-in PROM

Expand Down
4 changes: 1 addition & 3 deletions FEATURES.md
Original file line number Diff line number Diff line change
Expand Up @@ -674,6 +674,7 @@ and offline-tool controls are included so they are not mistaken for GUI settings
| `IRIS_BREAK_CPU` | 1 stops CPU after exception handling for diagnostic inspection. | [src/cpu/mips_exec.rs](src/cpu/mips_exec.rs) |
| `IRIS_CHD_DIFF_DIR` | Redirect CHD diff sidecars; GUI App Store sets a writable container directory. | [src/chd_disk.rs](src/chd_disk.rs) |
| `IRIS_CONST_DUMP` | Presence prints selected disassembly lines in constant-dedup tests. | [src/cpu/jitv2/mod.rs](src/cpu/jitv2/mod.rs) |
| `IRIS_AUDIO_DUMP` | File to record what every audio card plays, at the card's own rate before resampling, with or without a host sound device (HAL2, RAD1, the O2's MACE audio, the Tezro's ICE1724). Records are the rate and frame count (u32 each) then left, right i16 pairs; the O2 puts its output number (u8) in front. The layout is in the module's header. | [src/dev/audio_dump.rs](src/dev/audio_dump.rs) |
| `IRIS_CONST_MODE` | Select the opt-in constant-dedup codegen test shape. | [src/cpu/jitv2/mod.rs](src/cpu/jitv2/mod.rs) |
| `IRIS_CORPUS_DIR` | Corpus directory consumed by opt-in JIT corpus tests. | [src/cpu/jitv2/mod.rs](src/cpu/jitv2/mod.rs) |
| `IRIS_CRASH_DIAG` | off disables Windows crash handler installation. | [src/crash_diag.rs](src/crash_diag.rs) |
Expand All @@ -683,7 +684,6 @@ and offline-tool controls are included so they are not mistaken for GUI settings
| `IRIS_GUI_GL` | 1 selects the GUI GL capture path. | [iris-gui/src/framebuffer.rs](iris-gui/src/framebuffer.rs) |
| `IRIS_GPRCACHE` | Guest registers a JIT v2 function with a loop keeps in host registers, 0 to 31 (`j2 gprcache`; default 6, 10 on AArch64). | [src/cpu/jitv2/codegen.rs](src/cpu/jitv2/codegen.rs) |
| `IRIS_GPRCACHE_EVERYWHERE` | Presence caches registers in functions without a loop too. For test runs: slower to compile, no faster to run. | [src/cpu/jitv2/codegen.rs](src/cpu/jitv2/codegen.rs) |
| `IRIS_HAL2_CAPTURE` | HAL2 host audio capture output path. | [src/dev/hal2.rs](src/dev/hal2.rs) |
| `IRIS_HOSTGL_CALLS` | Substring filter for HostGL call logging (bounded log). | [iris-hostgl/src/service.rs](iris-hostgl/src/service.rs) |
| `IRIS_HOSTGL_INLINE` | 1 runs the HostGL service on the CPU thread instead of its own (for comparison; the CPU stalls while a buffer replays). | [iris-hostgl/src/lib.rs](iris-hostgl/src/lib.rs) |
| `IRIS_HOSTGL_NOSURFACE` | 1 disables CGL IOSurface presentation. | [iris-hostgl/src/backend/cgl.rs](iris-hostgl/src/backend/cgl.rs) |
Expand Down Expand Up @@ -715,8 +715,6 @@ and offline-tool controls are included so they are not mistaken for GUI settings
| `IRIS_MGRAS_REC` | Record IMPACT bus operations from power-on to this path. | [src/dev/mgras/mod.rs](src/dev/mgras/mod.rs) |
| `IRIS_O2_TRACE` | Trace the O2's rendering engine, GBE and CRIME from power-on to this path (`gputrace` builds). | [src/dev/o2gfx_trace.rs](src/dev/o2gfx_trace.rs) |
| `IRIS_MGRAS_TRACE` | Trace IMPACT accesses from power-on to this path (`gputrace` builds). | [src/dev/mgras/mod.rs](src/dev/mgras/mod.rs) |
| `IRIS_O2_AUDIO_CAPTURE` | O2 host audio capture output path (what the host plays). | [src/dev/mace_audio.rs](src/dev/mace_audio.rs) |
| `IRIS_O2_AUDIO_DUMP` | File to record the O2's audio output at the guest's rate, before resampling. | [src/dev/mace_audio.rs](src/dev/mace_audio.rs) |
| `IRIS_O2_AUDIO_TRACE` | Any presence traces every O2 audio register access and codec operation on stderr. | [src/dev/mace_audio.rs](src/dev/mace_audio.rs) |
| `IRIS_ODSY_BACKEND` | `inline` keeps the VPro fragment pipeline's shading and back end (depth, stencil, blend and stores) on the raster thread, with no worker pool. | [src/dev/odyssey/pipe/ring.rs](src/dev/odyssey/pipe/ring.rs) |
| `IRIS_ODSY_PIPE` | `old` draws VPro GL primitives through the previous rasteriser instead of the fragment pipeline that reads the board's own state words. | [src/dev/odyssey/pipe/mod.rs](src/dev/odyssey/pipe/mod.rs) |
Expand Down
3 changes: 2 additions & 1 deletion clanker/INDEX.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,8 @@ Non-graphics devices: MC/MEMCFG and RAM banks, HPC3, VDMA, EEPROMs and NVRAM, PS
| [virtio-9p-design.md](devices/virtio-9p-design.md) | design | virtio-9p: 9P2000.L server over a host directory; why every path is walked with `O_NOFOLLOW`, what a fid is, open items |
| [empty-gio-slot-reads-bus-error.md](devices/empty-gio-slot-reads-bus-error.md) | rule | Reads of an empty GIO slot end in a bus error (ABORT_EN); all ones made the PROM poll a phantom Newport 200 000 times headless |
| [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 the Octane's capture |
| [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"` |
| [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 |
Expand Down
29 changes: 13 additions & 16 deletions clanker/devices/audio-test-suite.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# The audio test suite (`test/audiotest`): rates x channels x widths

**Keywords:** audio,rad1,octane,o2,mace,sample rate,alSetParams,playaiff,sfplay,audiotest,sweep,capture,IRIS_OCTANE_AUDIO_WAV,IRIS_O2_AUDIO_DUMP
**Keywords:** audio,rad1,octane,o2,mace,sample rate,alSetParams,playaiff,sfplay,audiotest,sweep,capture,IRIS_AUDIO_DUMP

A guest program that plays a tone through the IRIX audio library, a sweep
that runs it and the stock players over every rate and format, and a host
Expand Down Expand Up @@ -46,23 +46,21 @@ Both machines need an IRIX 6.5 disk with the compiler and audio libraries
(`/usr/bin/cc`, `/usr/include/dmedia/audio.h`) and a config with the control
socket on (`ci = true`, `ci_socket`), plus a scratch volume for `iris-ci put`
(`[scsi.N]` with `scratch = true`). The emulator records the card's output
to a file:

- Octane: `IRIS_OCTANE_AUDIO_WAV=FILE` writes a 44.1 kHz stereo 16-bit WAV.
- O2: `IRIS_O2_AUDIO_DUMP=FILE` writes what MACE audio played at the guest's
own rate, before resampling, as records of output (u8), rate (u32) and
pair count (u32), then the pairs, little-endian. It needs no host sound
device. `check.py` measures each case at its records' rate.
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
rate.)

```sh
IRIS_OCTANE_AUDIO_WAV=/path/cap iris --config octane.toml & # or
IRIS_O2_AUDIO_DUMP=/path/cap iris --config o2.toml & # then: iris-ci start
IRIS_AUDIO_DUMP=/path/cap iris --config octane.toml & # or o2.toml
# then: iris-ci start
sh test/audiotest/host.sh /path/ci.sock /path/cap /path/out [EGREP_PATTERN]
python3 test/audiotest/check.py /path/cap /path/out/marks.txt
python3 test/audiotest/check.py /path/cap /path/out/marks.txt # --o2 for the O2
```

`check.py` tells the two formats apart by the first four bytes. The O2 needs
`overlay = true` on a disk in a writable directory (the disk image must be
The O2 needs `overlay = true` on a disk in a writable directory (the disk image must be
inside the run directory, e.g. hard-linked as `disk.raw`) and `ci = true`
to run headless. A full run takes about 4 minutes on the O2 and tens of
minutes on the Octane. The pattern picks cases (`'^gen_'`, `'sticky'`).
Expand Down Expand Up @@ -126,9 +124,8 @@ and MACE only moved that pointer on the service thread's passes, about
8 kHz where 16 or fewer were queued, and played a zero for each missing
one. Register reads now bring the rings up to date first.

Indy and Indigo2 (HAL2): not covered. HAL2 has only the host-side capture
(`IRIS_HAL2_CAPTURE`, raw stereo 16-bit at the host stream's rate, and only
with a host output device). A guest-rate dump like the O2's is planned.
Indy and Indigo2 (HAL2): not covered by the sweep. `IRIS_AUDIO_DUMP` records
codec A at its own rate, in the same record layout minus the output number.


## Gotchas
Expand Down
69 changes: 69 additions & 0 deletions clanker/devices/boot-chimes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Boot chimes: what each machine's PROM plays, and how to check it

**Keywords:** boot chime,startup tune,power-on tune,hal2,hpc3,rad1,mace,ice1724,ip22,ip24,ip28,ip30,ip32,IRIS_AUDIO_DUMP

iris carries no chime samples. Each PROM builds or decodes its tune in RAM with
its own code and starts the audio hardware playing it, so the sound is right
exactly when the CPU, FPU, DMA and audio models are. Nothing in iris picks or
substitutes a tune: another PROM revision plays another tune, and `prom = "<path>"`
selects one. A chime is a cheap, disk-free end-to-end check of those models.

| Machine | PROM | Tune | Rate |
|---|---|---|---|
| Indy IP24 | 070-9101-011 (embedded) | stored 4-bit ADPCM | 44.1 kHz |
| Indigo2 IP22 | 070-1367-012 (embedded) | synthesized plucked strings | 44.1 kHz |
| Indigo2 Impact IP28 | 070-1477-002 (embedded) | the same strings, R10000 build | 44.1 kHz |
| Octane IP30 | 4.17 (embedded) | stored 4-bit ADPCM | 32 kHz |
| O2 IP32 | 4.18 (embedded) | synthesized plucked strings | 34 kHz |

## Running a check

Run the machine headless with `IRIS_AUDIO_DUMP=<file>` set, wait for the tune,
and stop it:

```sh
IRIS_AUDIO_DUMP=chime.dump iris --headless --config indigo2.toml
```

The file holds what the audio card played, at the card's own rate before
resampling, with or without a host sound device: records of the rate (u32) and
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.)

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
is therefore checked by comparing the dump with an independent computation of
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 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).
- A blank Indy or Indigo2 settings block is seeded with the factory defaults, so
the first boot chimes (see [nvram-persistence.md](nvram-persistence.md)).
- The IP22 tune takes the longest to start, on the order of a minute or more on
the interpreter.

## What the repository itself verifies

The recorder and each card's hook have unit tests that assert the exact bytes
recorded: `cargo test -p iris --lib -- audio_dump hal2 rad1 mace_audio ice1724
host_pcm`. `test/audiotest/check.py` reads the same dump (`--o2` for the O2's
output byte) to judge the audio sweep; see [audio-test-suite.md](audio-test-suite.md).
The repository holds no model of any PROM tune.

## IP35

The IP35 PROM does not touch the ICE1724 on iris's Tezro model (observed in a
headless run).

## Other PROM revisions

Set `prom` to another revision and its chime plays; an older 5.1 IP22 PROM
plays a stored ADPCM tune where the embedded 5.3 one synthesizes.
5 changes: 2 additions & 3 deletions clanker/devices/ip32-o2-bringup-log.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,7 @@ Diagnostics that remain, all off unless set:
| `IRIS_IP32_RE_TRACE=N` | keep the last N rendering-engine register writes; save with the control port's `retrace` |
| `IRIS_IP32_MEC_TRACE` | Ethernet frames sent and received, and why a received one was dropped |
| `IRIS_O2_AUDIO_TRACE` | audio output activity, and each time a ring ran dry between sounds |
| `IRIS_O2_AUDIO_DUMP=file` | every sample each output plays, at the guest's rate, before resampling |
| `IRIS_O2_AUDIO_CAPTURE=file` | what the host plays, after resampling and mixing (raw 16-bit stereo) |
| `IRIS_AUDIO_DUMP=file` | every sample each output plays, at the guest's rate, before resampling |

## The assessment (2026-09-19)

Expand Down Expand Up @@ -1902,6 +1901,6 @@ was wrong, and what it turned out to be:
Instruments added for it: the control port's `pixel X Y` (one scanned-out
point: frame-buffer word, DID, mode-table entry, colour map, overlay) and
`unmapped`; `IRIS_O2_FRAG_PROBE=x,y` (every triangle fragment at a pixel,
with texture coordinates, LOD and level); `IRIS_O2_AUDIO_DUMP` (each
with texture coordinates, LOD and level); `IRIS_AUDIO_DUMP` (each
output's samples at the guest's rate); and the "ring ran dry" count in the
audio trace.
2 changes: 1 addition & 1 deletion clanker/hostgl/host-calls-hold-the-cpu-thread.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

**Symptom.** Sound crackles under a busy host-GL program (a demo's loud bass
notes, Quake), while the same program's sound alone plays clean. HAL2
captures (`IRIS_HAL2_CAPTURE`) show short runs of silence, ~20 a second.
captures of the host stream show short runs of silence, ~20 a second.

**Cause.** A host call (syscall 3000) is trapped in `exec_syscall` and
answered on the thread that runs the emulated CPU. Until it returns the
Expand Down
4 changes: 3 additions & 1 deletion docs/indigo2-ip22.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ No separate build is required — the same `iris` / `iris-gui` binary supports I

- **PROM:** IRIS tries `prom` from the config, then `070-1367-012.bin` in the
working directory, then falls back to an embedded Indigo2 PROM
(`data/proms/ip22.deflate`) — not the Indy one.
(`data/proms/ip22.deflate`) — not the Indy one. The boot chime comes from the PROM,
so setting `prom` to another revision plays that revision's tune: an older 5.1
PROM plays a stored tune, not the embedded PROM's synthesized one.
- **NVRAM and MAC:** the Indigo2 keeps its PROM environment and Ethernet address
in a 93CS56 serial EEPROM, not the Indy's DS1386. It is loaded from `nveeprom`
(default `nveeprom.bin`, `--nveeprom`) and written back with the monitor's
Expand Down
Loading