Skip to content

Fix expansion ROM and bus timing bugs for multi-card emulation#36

Open
BrentRector wants to merge 2 commits intoa2fpga:mainfrom
BrentRector:upstream-bugfixes
Open

Fix expansion ROM and bus timing bugs for multi-card emulation#36
BrentRector wants to merge 2 commits intoa2fpga:mainfrom
BrentRector:upstream-bugfixes

Conversation

@BrentRector
Copy link
Copy Markdown

Summary

Two latent bugs in the expansion ROM ownership logic that are exposed when multiple
emulated cards with C8 expansion ROM are active simultaneously, or when physical Apple II
cards with C8 ROMs share the CPLD bus.

Fix 1: INTC8ROM permanently blocking expansion ROM on Apple ][+

Files: hdl/memory/apple_memory.sv, boards/a2n20v2-Enhanced/hdl/memory/apple_memory.sv

On Apple ][+, the SLOTC3ROM soft switch doesn't exist (IIe-only) and defaults to 0. The
existing code unconditionally sets INTC8ROM=1 on every $C3xx access when SLOTC3ROM=0.
Since INTC8ROM=1 blocks io_strobe_n generation in the slotmaker framework, ALL
emulated cards' expansion ROM reads stop working — not just the card in slot 3.

This is latent because upstream slot 3 is typically empty. Any card assigned to slot 3 on a
][+ exposes the bug.

Fix: Add runtime ][+ vs IIe detection (is_iie flag). The IIe boot ROM writes to $C00x
soft switches within milliseconds of startup; a ][+ never does. Gate INTC8ROM with is_iie
and add missing else clause to clear INTC8ROM on non-C3 slot access (real IIe behavior).

Fix 2: SSC expansion ROM phi0 qualification and SLOTROM guard

File: hdl/ssc/super_serial_card.sv

Two issues in SSC's C8-space ownership logic:

  1. Missing phi0 gate on C8S2: When other cards sharing the CPLD transition their rd_en_o
    from active to tristate, PCB bus transceiver glitches create address transients visible to
    the FPGA at 54 MHz. Without phi0 qualification, the SSC's $CFFF detection fires on these
    transients, spuriously clearing C8S2 mid-execution of the SSC expansion ROM.

  2. Missing SLOTROM guard on ENA_C8S: Once C8S2 is set via $C2xx, the SSC responds to
    ALL $C800-$CFFF reads even when another slot should be active. On real hardware with
    per-slot bus transceivers this doesn't matter, but on the shared CPLD it causes bus
    contention.

Both bugs are latent because the SSC is the only upstream emulated card with C8 expansion ROM.
With only one C8-capable card, no other card's rd_en_o transitions create glitches, and stale
C8S2 never causes contention. Adding any second C8-capable card (emulated or physical) exposes
both bugs.

How Discovered

Found during Videx VideoTerm emulation development. Apple Pascal 1.3 boot hung
non-deterministically during SSC FINIT — the Videx card's bus transceiver transitions
cleared SSC's C8S2 mid-execution, and stale C8S2 caused the SSC to respond during
Videx's C8-space reads.

Test Plan

  • Apple ][+ hardware test with emulated cards: SSC (slot 2), Videx (slot 3),
    Mockingboard (slot 4), SuperSprite (slot 7)
  • Apple Pascal 1.3 boots 100% with all cards enabled
  • PR#3 enters 80-column mode correctly (slot 3 access triggers INTC8ROM path)
  • Original Videx VideoTerm Demo application runs correctly
  • SSC expansion ROM accessible when SLOTROM=2, correctly inhibited for other slots

BrentRector and others added 2 commits February 23, 2026 01:14
Problem:
On Apple ][+, the SLOTC3ROM soft switch doesn't exist (it is IIe-only)
and defaults to 0. The existing INTC8ROM logic unconditionally sets
INTC8ROM=1 on every $C3xx access when SLOTC3ROM=0:

    if (!a2mem_if.SLOTC3ROM && (a2bus_if.addr[15:8] == 8'hC3))
        INTC8ROM <= 1'b1;

On a ][+, this means the first PR#3 or any $C3xx access permanently
sets INTC8ROM=1. Since INTC8ROM=1 blocks io_strobe_n generation in
the slotmaker framework, ALL emulated cards' expansion ROM ($C800-$CFFF)
reads stop working — not just the card in slot 3.

A second issue: INTC8ROM was never cleared when a different slot's $Csxx
was accessed. On a real IIe, accessing any slot other than 3 in the
$C1xx-$C7xx range clears INTC8ROM. The missing else clause meant
INTC8ROM remained stale across slot transitions.

How discovered:
This bug was found while adding Videx VideoTerm emulation to slot 3.
PR#3 on the Apple ][+ test system triggered $C3xx access, permanently
setting INTC8ROM=1. The SSC card in slot 2 then failed to serve its
expansion ROM, causing Apple Pascal 1.3 boot to hang during SSC FINIT.

The bug is latent in the upstream codebase — it exists regardless of
Videx. Upstream slot 3 is typically empty, so INTC8ROM is never
triggered. Any card assigned to slot 3 on a ][+ would expose it.
Physical Apple II cards with C8 ROMs in other slots would also be
affected, as INTC8ROM blocks io_strobe_n system-wide.

Fix:
Add runtime ][+ vs IIe detection via an is_iie flag. The IIe boot ROM
writes to $C00x soft switches within milliseconds of startup; a ][+
never touches these addresses. Gate INTC8ROM with is_iie so it is
never set on ][+ (matching real ][+ hardware, which has no INTC8ROM
mechanism at all). Add else clause to clear INTC8ROM on non-C3 slot
access, matching real IIe behavior.

The Enhanced board variant is always IIe (has real SLOTC3ROM via
SWITCHES_IIE[5]), so it only needs the missing else-clear, not is_iie
detection.

Testing:
Verified on Apple ][+ hardware (A2N20v2 board) with emulated cards in
slots 2 (SSC), 3 (Videx VideoTerm), 4 (Mockingboard), and 7
(SuperSprite). All expansion ROMs function correctly after fix.
Apple Pascal 1.3 boots and runs all applications 100%. Original Videx
VideoTerm Demo application runs correctly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Problem 1 — Missing phi0 qualification on C8S2 ownership:
The SSC's C8-space ownership flag (C8S2) is set on $C2xx access and
cleared on $CFFF access. The case block that monitors these addresses
runs on every clk_logic edge without phi0 qualification:

    always @(posedge a2bus_if.clk_logic) begin
        ...
        end else begin  // <-- no phi0 gate
            case (a2bus_if.addr[15:8])

The A2FPGA emulates multiple cards through a single CPLD (XC9572XL).
When any emulated card transitions its rd_en_o from active to tristate,
the PCB bus transceiver creates real address bus glitches visible to the
FPGA at 54 MHz sampling rate. Without phi0 qualification, the SSC's
$CFFF detection fires on these transients, spuriously clearing C8S2
mid-execution of the SSC expansion ROM. This causes open bus reads and
crashes the SSC FINIT routine.

Fix: Gate the case block with phi0. Address bus transients from bus
transceiver glitches only occur outside phi0 (during drive/tristate
transitions at phi boundaries). This is the same pattern needed by any
card that monitors bus addresses for ownership state changes.

Problem 2 — Missing SLOTROM guard on ENA_C8S:
The expansion ROM enable (ENA_C8S) only checks C8S2 and INTCXROM:

    assign ENA_C8S = {(C8S2 & !a2mem_if.INTCXROM), ...} == 6'b111001;

Once C8S2 is set via $C2xx, the SSC responds to ALL $C800-$CFFF reads
even when another slot should be active. On real hardware with per-slot
bus transceivers, a stale C8S2 doesn't cause problems because each
card's bus drivers are electrically independent. On the A2FPGA, where
all emulated cards share one CPLD, a stale C8S2 means the SSC drives
the data bus for another slot's $C800-$CFFF reads, causing bus
contention in the CPLD's output mux.

Fix: Add SLOTROM guard so the SSC only responds when slot 2 was the
most recently accessed $Csxx slot: && (a2mem_if.SLOTROM == 3'd2).

How discovered:
Both bugs were found during Videx VideoTerm emulation development. The
SSC is the only upstream emulated card with C8 expansion ROM. With only
one C8-capable card: (a) no other card's rd_en_o transitions create bus
transceiver glitches that could clear C8S2, and (b) stale C8S2 never
causes contention because no other card claims $C800-$CFFF. Adding any
second C8-capable card (emulated or physical) exposes both bugs.

The phi0 bug manifested as non-deterministic SSC FINIT crashes during
Apple Pascal 1.3 boot. The Videx card's rd_en_o transitions created bus
transceiver glitches that looked like $CFFF to the FPGA, clearing C8S2
mid-SSC expansion ROM execution. With phi0 qualification, the SSC
progressed further but still failed due to the OE timing issue (separate
fix in apple_bus.sv).

Testing:
Verified on Apple ][+ hardware (A2N20v2 board) with emulated cards in
slots 2 (SSC), 3 (Videx), 4 (Mockingboard), and 7 (SuperSprite).
Apple Pascal 1.3 boots 100%. SSC expansion ROM reads work correctly
when SLOTROM=2, correctly inhibited when other slots are active.
Original Videx VideoTerm Demo application runs correctly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant