Skip to content

Zorro II AUTOCONFIG for Drivers

An Amix driver does not hard-code where its expansion board lives. The Amiga AUTOCONFIG protocol assigns each Zorro II board a base address at every reset, and the driver asks the system "where did my board land?" at run time โœ…. In the kernel that question is answered by the Amix-specific helper autocon(product_id, dev, &board, &dummy) ๐ŸŸก; from user space you answer it yourself by mmap()-ing /dev/mem and walking the AutoConfig nibble registers โ€” exactly what the lszorro scanner does โœ….

Two hard limits frame everything on this page:

  • Zorro II only. Amix's memory-mapping layer cannot address Zorro III space, and that source was never shipped, so it can't be community-fixed โœ…. See Hardware.
  • AutoConfig boards only. RAM and accelerator boards with no AutoConfig ROM are invisible to this mechanism โ€” lszorro cannot see them โœ….

Grounding: this page is built from ยง2 and ยง6 of the research brief, the Ditto driver paper, and the asokero/lszorro-amix source. The autocon() signature is repo-confirmed but community-tier ๐ŸŸก โ€” do not treat it as primary-verified.

What AUTOCONFIG does, and why drivers care

AUTOCONFIG is the Amiga's plug-and-play scheme for Zorro expansion cards. At power-on / reset, the Amiga bootstrap walks the expansion bus and, for each board it finds, assigns a base address from the relevant address pool and tells the board to relocate there โœ…. The board exposes a small read-only configuration ROM (the "nibble registers") describing who made it, what it is, and how much space it wants; the bus logic uses that to place it.

Because the placement happens fresh at every reset, a driver written for a fixed address would break the moment the bus topology changes (another card added, slot order changed). So the contract is:

  1. The board declares a manufacturer ID (a.k.a. mfr / vendor) and a product ID in its AutoConfig ROM.
  2. The system places the board and records its assigned base.
  3. The driver looks the board up by (mfr, product) and gets back the base address it was assigned โœ….

Amix reads the addresses AUTOCONFIG assigned via the kernel autocon() interface โœ…; autocon() itself is the Zorro II discovery path for in-kernel drivers ๐ŸŸก (brief ยง3, ยง5).

The kernel side: autocon()

Amix drivers discover their board with the Amix-specific kernel call ๐ŸŸก (repo-confirmed, not primary-verified):

/* Amix-specific Zorro II board discovery โ€” signature per modern repos */
int autocon(int product_id, int dev, caddr_t *board, int *dummy);
Argument Meaning
product_id the board's AutoConfig product ID to match
dev the device / unit being brought up
&board out: receives the assigned Zorro II base address (kernel-mapped)
&dummy out: an extra value the call fills in (e.g. a fallback / placeholder slot)

A driver typically calls autocon() from its init or open routine, then keeps the returned board pointer for all subsequent register access. For example, the VA2000 framebuffer driver uses autocon() (alongside uiomove() and copyin/copyout) to locate its 4 MB Zorro II window before mapping the framebuffer โœ…. The Hydra DLPI driver layers a three-method detect on top โ€” autocon()/bootinfo (with address validation), then a direct Zorro II I/O-slot probe, then a memory-space probe โ€” because the bootinfo ConfigDev table can be corrupt on Amix 2.1p2 โœ….

autocon() is part of the same Amix DDI/DKI surface a driver draws on (copyin/copyout, uiomove, sleep/wakeup, spl2/splx, โ€ฆ); see Key kernel APIs in the driver model.

Note: the four-argument shape above comes from the modern community repos, not from the Ditto paper, which predates them. Carry it as ๐ŸŸก and verify against your kernel headers before relying on the exact argument order.

Zorro III boards and the real "Zorro II only" wall

๐Ÿ”ด "Zorro II only" is imprecise about autocon() itself. autocon() searches the same bootinfo.autocon[NAUTO] table for any AutoConfig board and returns the assigned base for a matched product id โ€” including Zorro III boards โœ… (first-party, from the A4091-on-Amix project; autocon() in amiga/kernel/support.c). The match is purely on (er_Manufacturer, er_Product); nothing in the table search rejects a Zorro III address. So a Zorro III board does AutoConfigure and autocon() does hand back its base.

๐Ÿ”ด The actual wall is the 68030 Transparent-Translation gap, not autocon(). Amix maps physical space with two Transparent-Translation registers (amiga/ml/ttrap.s) โœ…:

TT register Value Physical range it makes directly addressable
tt0 0x003F0143 0x00000000โ€“0x3FFFFFFF
tt1 0x807F0143 0x80000000โ€“0xFFFFFFFF
(gap) โ€” 0x40000000โ€“0x7FFFFFFF unmapped

A Zorro II board (โ‰ค 24-bit, e.g. the A3000 internal SCSI at 0xDD0000) lives inside TT0, so a stock WD33C93 driver can dereference the autocon() base directly โœ…. A Zorro III board such as the A4091 (physical base 0x40000000) lands in the unmapped gap โ€” autocon() returns the right address, but the CPU cannot reach it without a page mapping โœ…. Widening TT0 is unsafe: amiga/ml/syms.s places the page-mapped u-area at 0x40000000.

The fix is the kernel primitive sptalloc(npages, prot, pfn, flag), which page-table-maps physical pages into kernel VA โœ…. A Zorro III driver takes the autocon() base and sptalloc-maps it before any register access:

extern caddr_t sptalloc();
acfg = (volatile uchar *)sptalloc(1, PG_V, phystopfn((paddr_t)base), 0);              /* board base  */
siop = (volatile uchar *)sptalloc(1, PG_V, phystopfn((paddr_t)base + 0x00800000), 0); /* chip regs   */

See the A4091/53C710 driver for the full Zorro III bring-up (this is exactly how the A4091 SCSI host adapter, product id 0x02020054, is made addressable from Amix).

autocon() and the phantom A3000 internal SCSI

๐Ÿ”ด What was hardcoded, and why it mis-orders cards. The A3000 internal SCSI (Commodore DMAC + WD33C93 at 0xDD0000) is not an AutoConfig board, so it never appears in bootinfo.autocon[]. Historically, autocon() in amiga/kernel/support.c faked one in: whenever RAM extended past 7 MB it returned a phantom A3000 SCSI at 0xDD0000 regardless of whether the hardware exists โœ… (first-party). The original special case:

if (index==0 && end > (char *)0x07000000 && (pc==0x0202F003)) { *bp = 0xdd0000; return 1; }

On a machine without the A3000 SCSI (or a real A4000, which has none) this phantom still claims queue[0] (card 0) and shoves the next SCSI card to card 1. That mis-orders the SCSI card table โ€” and because the compiled-in root device decodes to card 0, the kernel sends the root read to non-existent hardware. (For the boot-time consequences and the panic chain it produces, see the boot process.)

Chipset-gated WD33C93 auto-detection (the fix)

โœ… What it actually does now (first-party, from the A4091-on-Amix project โ€” full source in src/kernel-patches/support.c). The phantom is replaced with a chipset gate + a WD33C93 write/readback probe, so the A3000 SCSI is registered only when it is genuinely present:

if (index==0 && (pc==0x0202F003)) {                 /* A3000 internal SCSI: not an AutoConfig board */
    int a3kscsi = 0;
    unsigned short vposr = *(volatile unsigned short *)0xDFF004;   /* custom chip, always present, bus-safe */
    if ((((int)vposr >> 8) & 0x7F) < 0x22) {        /* ECS/OCS Agnus (<0x22) = A3000-class; AGA Alice (>=0x22) = A4000 */
        volatile unsigned char *sasr = (unsigned char *)0xDD0041;  /* WD33C93 SASR */
        volatile unsigned char *scmd = (unsigned char *)0xDD0043;  /* WD33C93 SCMD */
        *sasr = 0x02; *scmd = 0x55;  *sasr = 0x02;                 /* write/readback Timeout-Period reg */
        if (*scmd == 0x55) { *sasr = 0x02; *scmd = 0xAA; *sasr = 0x02; if (*scmd == 0xAA) a3kscsi = 1; }
    }
    if (a3kscsi) { *bp = 0xdd0000; return 1; }       /* present -> register */
    /* absent -> fall through; autocon returns 0; sd.c never registers it */
}

How the two steps work โœ…:

  • Chipset gate (the safety mechanism). Read VPOSR (0xDFF004) โ€” a custom-chip register that is always present and bus-safe to read on any Amiga. The identification field is bits 8โ€“14 (mask the toggling LOF bit at bit 15, i.e. (vposr >> 8) & 0x7F): < 0x22 = ECS/OCS Agnus (A3000-class), โ‰ฅ 0x22 = AGA Alice (A4000). On AGA the whole block is skipped, so the kernel never reads 0xDD0000 โ€” eliminating any bus-fault at an address that does not decode on an A4000.
  • WD33C93 probe (on A3000-class only). Write 0x55 then 0xAA to the WD33C93 Timeout-Period register (indirectly, via its SASR/SCMD ports at 0xDD0041 / 0xDD0043) and read each back. The board at 0xDD0000 is registered only if the chip echoes both values.

๐ŸŸก Emulation caveat (carry honestly). Amiberry's open bus at 0xDD0000 echoes writes, so the write/readback false-positives when scsi_a3000=false โ€” in emulation "ECS always registers" even with no WD33C93 attached. This is harmless for the real targets (a physical A3000 always has the WD33C93, and a real A2500's empty bus would correctly fail the probe). The chipset gate is what makes the A4000 / AGA case correct and safe; the WD33C93 readback can only be fully trusted on real hardware.

The Zorro II ID nibble encoding

To match a board you must read its AutoConfig ROM, and that ROM is laid out in a deliberately awkward nibble format. Understanding it is what lets lszorro (and any userspace probe) decode (mfr, product) correctly โœ….

The encoding rules, as used by lszorro โœ…:

  • Two nibbles per logical byte. Each logical configuration byte is split across two physical addresses (a high-nibble slot and a low-nibble slot).
  • Upper nibble lives in D15โ€“D12. The meaningful nibble is read from the top four data-bus bits of the 16-bit word at that offset, not the low bits.
  • Most fields are stored ones-complement (inverted). After extracting a nibble you must invert it to recover the true value โ€” with the conventional exception that the very first register pair is not inverted (so software can tell it is reading a real AutoConfig ROM and which way is up). Treat "most fields are ones-complement" as the working rule and special-case the leading bytes per the standard Zorro layout โœ….

In other words: to read one configuration byte you read two 16-bit words, take bits D15โ€“D12 of each, combine high-nibble and low-nibble, and (for inverted fields) take the ones-complement. From the reconstructed bytes you obtain the manufacturer ID, the product ID, the board's size/type flags, and any optional serial / boot-ROM fields.

This is purely a property of Zorro II AutoConfig ROMs โ€” Amix imposes nothing extra. lszorro matches the decoded (mfr, product) against a 461-entry ID database lifted from the Linux kernel Zorro list to print human-readable names โœ….

The userspace side: scanning via /dev/mem

You do not need a kernel driver to enumerate boards. lszorro runs entirely in user space by memory-mapping physical address space through /dev/mem โœ…:

# Build natively on Amix (single source file)
cc lszorro.c -o lszorro

# Must run as root: /dev/mem is privileged
./lszorro

The mechanism โœ…:

  1. Open /dev/mem (requires root โ€” it is raw physical memory).
  2. mmap() a small window โ€” 128 bytes (0x80) โ€” over each candidate board slot.
  3. Walk both Zorro II address pools (table below), reading the AutoConfig nibble registers at each slot.
  4. Decode each populated slot's (mfr, product) per the nibble encoding above and look it up in the ID database.

Zorro II address ranges to scan

These are the physical ranges lszorro walks โœ…:

Pool Address range Notes
I/O space 0xE90000โ€“0xEFFFFF Zorro II expansion I/O board area
Memory space 0x200000โ€“0x9FFFFF Zorro II expansion memory board area

Map a 0x80-byte window at each slot, read the configuration registers, and move on. Boards that present only registers (no AutoConfig ROM in the usual place) can still be fingerprinted โ€” lszorro detects register-only boards such as the VA2000 by their signature โœ…. What it cannot see are boards with no AutoConfig presence at all, i.e. RAM and accelerator boards โœ….

Warning: /dev/mem is unmediated physical memory. Reading the AutoConfig pools is safe; writing anywhere through /dev/mem can corrupt the running system. Keep probes read-only.

Worked example: lszorro end to end

lszorro is the canonical worked example for everything above โ€” an lspci-style enumerator for Amix โœ…. The full pipeline:

  1. Open /dev/mem as root.
  2. Iterate the I/O pool 0xE90000โ€“0xEFFFFF and the memory pool 0x200000โ€“0x9FFFFF, mmap()-ing a 0x80-byte window per slot.
  3. Read the AutoConfig nibble registers at each slot: two nibbles per byte, upper nibble in D15โ€“D12, ones-complement on most fields.
  4. Decode (mfr, product), size and type; fingerprint register-only boards (e.g. the VA2000).
  5. Resolve names against the 461-entry Linux-derived Zorro ID database and print a board-per-line listing.

Concrete IDs you will see decoded (all โœ… from the repos):

Board mfr (vendor) product What the driver does with it
MNT VA2000 0x6D6E 0x01 4 MB Zorro II window; framebuffer driver maps regs 0x000000โ€“0x00FFFF, FB 0x010000โ€“0x3FFFFF (see VA2000 case study)
Hydra AmigaNet 2121 (0x0849) 1 (0x0001) combined AutoConfig ID 0x08490001; STREAMS/DLPI net driver hya (see Hydra case study)

So autocon() (in-kernel) and lszorro (userspace) are two readers of the same AutoConfig data: the kernel helper hands a driver its assigned base by product ID; the scanner walks the pools itself to list everything. If you are bringing up a new board, run lszorro first to confirm the board AutoConfigured and to read its real (mfr, product), then wire those numbers into your driver's autocon() call and its cdevsw[]/bdevsw[] slot.

See also

Sources

  • sources/research-brief.md ยง2 (Zorro II only; no Zorro III mapping; supported expansion), ยง3 (AUTOCONFIG assigns Zorro II addresses at reset; autocon() consumed by the kernel), ยง5 (autocon(product_id, dev, &board, &dummy) ๐ŸŸก repo-confirmed; driver API surface), ยง6 (lszorro mechanism, ranges, nibble encoding, ID database; VA2000 and Hydra AutoConfig IDs).
  • asokero/lszorro-amix repo โ€” /dev/mem mmap() scan, 0x80-byte windows, I/O 0xE90000โ€“0xEFFFFF / mem 0x200000โ€“0x9FFFFF, AutoConfig nibble decode, 461-entry ID DB, register-only fingerprinting: https://github.com/asokero/lszorro-amix
  • asokero/va2000-amix repo โ€” VA2000 AutoConfig mfr 0x6D6E product 0x01, 4 MB Zorro II window, autocon() usage: https://github.com/asokero/va2000-amix
  • isoriano1968/hydra-amix repo โ€” Hydra AutoConfig ID 0x08490001 (2121/1), autocon() plus three-way detect: https://github.com/isoriano1968/hydra-amix
  • The A4091-on-Amix project โ€” NOTES.md ยง2, ยง6, ยง7 (reproduced locally โœ…; Zorro III + TT gap, sptalloc(), phantom-A3000, chipset-gated WD33C93 detection) and src//tools/.
  • src/kernel-patches/support.c โ€” the full autocon() with the chipset-gated WD33C93 auto-detection replacing the phantom-A3000 special case (TT registers tt0=0x003F0143 / tt1=0x807F0143 from amiga/ml/ttrap.s; A4091 product id 0x02020054).
  • a4091.device open-source project: https://github.com/A4091/a4091-software (A4091 ROM + ncr53cxxx SCRIPTS assembler).
  • Ditto, Writing Amix Device Drivers, 1990 European Amiga Developer's Conference โ€” driver model and kernel API context (see bibliography).
  • amigaunix.com โ€” historical and hardware reference: https://www.amigaunix.com/doku.php/home