Skip to content

6502 reset sequence does not set the I flag, so a pending IRQ is taken in place of the first instruction of the reset handler #641

Description

@rob-smallshire

Summary

When the emulated 6502 is reset with IRQ asserted and the I flag clear, the CPU takes the IRQ before executing the first instruction at the reset vector's target. A real 6502 sets I as part of the reset sequence, so a pending IRQ stays masked until the reset handler clears I.

It is benign in normal use. On a healthy machine the spurious IRQ is serviced by the intact MOS handler, which RTIs to the stacked PC, which is the reset entry, and the reset then proceeds. It only matters when the IRQ vectors or handler are corrupt.

I don't think this causes any real problems in b2, but it is a software detectable difference in behaviour between the emulator and real hardware.

Reproduction

Attached SSD, CHAIN"RSTIRQ", wait for the message, press BREAK on its own, then PRINT ?&70,?&76. Model B, no second processor. Listing:

10 REM DOES RESET TAKE A PENDING IRQ?
20 DIM C% 80:?&70=0:?&76=0:?&71=?&204:?&72=?&205:?&74=?&FFFC:?&75=?&FFFD
30 FOR P=0 TO 2 STEP 2:P%=C%:[OPT P
40 .H STX &73:TSX:LDA &103,X:CMP #&80:BCC N:CMP #&C0:BCS N:LDA #1:STA &76
50 .N LDA &103,X:CMP &75:BNE K:LDA &102,X:CMP &74:BNE K:INC &70
60 .K LDX &73:JMP (&71)
70 .I SEI:LDA #H MOD 256:STA &204:LDA #H DIV 256:STA &205:CLI:RTS
80 ]:NEXT:CALL I:T%=TIME:REPEAT UNTIL TIME>T%+100
90 PRINT "PRESS BREAK, THEN TYPE  PRINT ?&70,?&76"

It hooks IRQ1V with a handler that increments &70 whenever the return address stacked by an interrupt equals the reset vector's target, i.e. an IRQ was taken before the first reset instruction ran, then chains to the original handler. &76 is a control, set when an interrupt arrives while BASIC is running, which proves the handler's stack offsets are right. All interrupts stay enabled, so the 100 Hz timer guarantees one is pending when BREAK is released. A soft BREAK restores IRQ1V and preserves zero page, so the hook removes itself and the result survives.

Results

Machine Result
BBC Model B, SYU6502A (NMOS), real hardware 0 1
BBC Master Compact, 65SC12 (CMOS), real hardware 0 1
BeebEm 0 1
Beebium (your vendored 6502 core) 3 1
b2 2 1

Expected 0 1. The non-zero first number is the defect; the count is just how many sources were pending.

A more dramatic demonstration: disable every System VIA interrupt except the keyboard's (?&FE4E=&7E), point IRQ1V at a &02 byte, loop forever, then hold BREAK, tap SPACE while it is held, and release. b2 lands on the jam opcode and the machine is dead, where real hardware shows the normal banner. A second BREAK then recovers b2, because the IRQ sequence that led to the jam set I and b2 preserves P across reset. That asymmetry points at the missing flag rather than at the corrupted vector.

Cause

src/6502/6502_gen.cpp, the reset definition:

G("Reset", "Interrupts", {Ri("pc", "data!", nullptr), Rd("sp--", "pch", nullptr), Rd("sp--", "pcl", nullptr), Rd("sp--", "data", nullptr), Ra("resl", "pcl", nullptr), Ra("resh", "pch", nullptr)});

This doesn't set p.bits.i. The generated Cycle5_Reset calls CheckForInterrupts, which sees irq_flags != 0 && !p.bits.i and clears d1x1, so M6502_NextInstruction starts the interrupt sequence instead of fetching the first opcode. The hand-written Cycle4_Interrupt has s->p.bits.i = 1 but the reset sequence has no equivalent. BBCMicro.cpp's BREAK handling calls M6502_Reset and leaves P alone, which is correct in itself, so whether the defect triggers depends on whether the I flag happened to be clear when BREAK was pressed.

Suggested fix

Set I on the FFFC (vector-low) cycle of the reset sequence, before the CheckForInterrupts on the following cycle, mirroring Cycle4_Interrupt.

Apparently, CMOS parts also clear D at that point, while NMOS leaves D unchanged, so that part needs to be per-processor type.

References

  • MOS Technology, MCS6500 Microcomputer Family Programming Manual (January 1976), s3.2 (page 25): "The interrupt disable, I, is set by the microprocessor during reset and interrupt commands." s9.3: (page 127) "the only automatic operations of the microprocessor during reset are to turn on the interrupt disable bit and to force the program counter to the vector location specified in locations FFFC and FFFD".
  • WDC W65C02S datasheet, s3.11: the Decimal and Interrupt-disable bits "are initialized by hardware" on reset (D = 0, I = 1). Its NMOS comparison table gives D as indeterminate after reset on NMOS.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    accuracyissue with emulation accuracy

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions