Summary
Make each slot's occupant user-selectable: a dropdown in the Slots section of the Hardware tab offering any supported card for any slot, with default assignments modeling where people actually installed each card in period.
An Apple II slot is generic. Fixing each card to one slot per machine profile is the thing that's actually unfaithful.
Background
Today the Hardware tab renders slots as a checkbox tree — a slot's card can be enabled or removed, but not changed. Which card lives in which slot is baked into the machine profile JSON:
| Profile |
Slot 1 |
Slot 4 |
Slot 6 |
| Apple ][ |
parallel-printer |
— |
disk-ii |
| Apple ][+ / //e / //e Enhanced |
parallel-printer |
mockingboard |
disk-ii |
The emulation layer is already slot-agnostic — every card takes its slot from config and computes its own $Cn00 base:
Disk2Controller::Create — config.hasSlot ? config.slot : 6
PrinterCard::Create — defaults to 1
Acia6551::Create — derives its base from config.slot
MockingboardCard::Create — passes the slot straight through
Nothing hardcodes a slot. "Any card in any slot" is already true below the UI.
Scope
1. Card descriptors in the registry. ComponentRegistry is typeName -> FactoryFunc and nothing else. Populating a dropdown needs per-type metadata: display name ("Mockingboard C (sound + speech)", not "mockingboard"), which slots are legal, whether duplicates are allowed, whether a slot ROM is required, and a recommended default slot.
2. Slot composition becomes user-editable state. This is the substantive change. SettingsPanelState::BuildJson rebuilds the slots array from the machine profile with only the enabled bit overlaid — the device string is cloned verbatim, and SetHardwareEnabled is the only mutator. Making the occupant editable means the override JSON starts carrying slot composition, which touches the merge, the dirty check, and the reset-required rule.
3. An in-row dropdown in the tree. DxuiTreeView paints checkboxes and capability flags. An interactive dropdown inside a tree row is real Dxui work, and its popup must escape the page's clipping bounds — HardwarePage already solves that for the machine and speed dropdowns via SetPopupHost, so there's a pattern to follow, though a tree row is a harder host than a fixed-position control.
4. Period-typical defaults. Slot assignments that model where cards actually went — printer in 1, Mockingboard in 4, Disk II in 6 — so the out-of-box machine matches what software expects and the dropdown is an override rather than a requirement.
Constraints worth surfacing rather than enforcing
The hardware doesn't restrict placement; software convention does, and that's a UX problem rather than a correctness one:
- Convention is load-bearing. Mockingboard players overwhelmingly assume slot 4, DOS/ProDOS boot assumes slot 6, printer drivers assume slot 1. A user who moves the Mockingboard to slot 5 gets a silent machine and no explanation. Prefer a "recommended slot" hint over a hard restriction.
- The //c has no slots. Its equivalents are soldered down.
CapabilityFlag::PlatformLocked + lockReason already exist for exactly this — the dropdown should be absent or locked there.
- The shell caches one pointer per card type.
EmulatorShell holds a single diskController / mockingboard / etc. A free dropdown permits configurations that were legal on real hardware but that this wiring doesn't expect — two Mockingboards (people genuinely did this), or a Disk II in slot 3. Either constrain duplicates in the descriptor or teach the shell to resolve by type across slots.
Relationship to the Mockingboard C work
This is where a user picks between the Mockingboard A and the Mockingboard C. That work does not depend on this issue — it selects its variant by machine configuration — but this is what exposes the choice properly, alongside every other card.
Sizing note: the candidate list today is four cards plus "empty". The payoff grows as cards are added, which the disk specs (020/021/022) and the platform roadmap will do.
Acceptance Criteria
Summary
Make each slot's occupant user-selectable: a dropdown in the Slots section of the Hardware tab offering any supported card for any slot, with default assignments modeling where people actually installed each card in period.
An Apple II slot is generic. Fixing each card to one slot per machine profile is the thing that's actually unfaithful.
Background
Today the Hardware tab renders slots as a checkbox tree — a slot's card can be enabled or removed, but not changed. Which card lives in which slot is baked into the machine profile JSON:
The emulation layer is already slot-agnostic — every card takes its slot from config and computes its own
$Cn00base:Disk2Controller::Create—config.hasSlot ? config.slot : 6PrinterCard::Create— defaults to 1Acia6551::Create— derives its base fromconfig.slotMockingboardCard::Create— passes the slot straight throughNothing hardcodes a slot. "Any card in any slot" is already true below the UI.
Scope
1. Card descriptors in the registry.
ComponentRegistryistypeName -> FactoryFuncand nothing else. Populating a dropdown needs per-type metadata: display name ("Mockingboard C (sound + speech)", not"mockingboard"), which slots are legal, whether duplicates are allowed, whether a slot ROM is required, and a recommended default slot.2. Slot composition becomes user-editable state. This is the substantive change.
SettingsPanelState::BuildJsonrebuilds theslotsarray from the machine profile with only theenabledbit overlaid — thedevicestring is cloned verbatim, andSetHardwareEnabledis the only mutator. Making the occupant editable means the override JSON starts carrying slot composition, which touches the merge, the dirty check, and the reset-required rule.3. An in-row dropdown in the tree.
DxuiTreeViewpaints checkboxes and capability flags. An interactive dropdown inside a tree row is real Dxui work, and its popup must escape the page's clipping bounds —HardwarePagealready solves that for the machine and speed dropdowns viaSetPopupHost, so there's a pattern to follow, though a tree row is a harder host than a fixed-position control.4. Period-typical defaults. Slot assignments that model where cards actually went — printer in 1, Mockingboard in 4, Disk II in 6 — so the out-of-box machine matches what software expects and the dropdown is an override rather than a requirement.
Constraints worth surfacing rather than enforcing
The hardware doesn't restrict placement; software convention does, and that's a UX problem rather than a correctness one:
CapabilityFlag::PlatformLocked+lockReasonalready exist for exactly this — the dropdown should be absent or locked there.EmulatorShellholds a singlediskController/mockingboard/ etc. A free dropdown permits configurations that were legal on real hardware but that this wiring doesn't expect — two Mockingboards (people genuinely did this), or a Disk II in slot 3. Either constrain duplicates in the descriptor or teach the shell to resolve by type across slots.Relationship to the Mockingboard C work
This is where a user picks between the Mockingboard A and the Mockingboard C. That work does not depend on this issue — it selects its variant by machine configuration — but this is what exposes the choice properly, alongside every other card.
Sizing note: the candidate list today is four cards plus "empty". The payoff grows as cards are added, which the disk specs (020/021/022) and the platform roadmap will do.
Acceptance Criteria
$Cn00page