Bug in 6502 BRK.

Programming the 6502 microprocessor and its relatives in assembly and other languages.
orac81
Posts: 53
Joined: 20 Apr 2024
Location: uk

Bug in 6502 BRK.

Post by orac81 »

I was thinking about the code for the AttackUfo game (6502+pre Vic20 6560).

https://sleepingelephant.com/ipw-web/bu ... php?t=8775

The game code uses the BRK as a JSR shortcut, with parameters inline after. Its probably some sort of compiled code.
I understand the early NMOS 6502s had a bug, if an IRQ happened at the same time as the BRK, the CPU just serves the IRQ and forgets the BRK. Now, if you have a CBM c64/v20/pet type 1/60 sec IRQ, could the IRQ code fix this, by looking at the RTI address on the stack, and if it points to a BRK, knock the stack address back by one?
Also how many 6502s had the bug? Is it possible for code to test for it?
User avatar
BigEd
Posts: 11463
Joined: 11 Dec 2008
Location: England
Contact:

Re: Bug in 6502 BRK.

Post by BigEd »

Maybe check these previous threads
* BRK/IRQ/NMI
and
* Question IRQ interruption during BRK execution

My vague feeling is yes, the ISR can mitigate the bug. But I might be wrong. I think the bug is present in all NMOS 6502 cores.
User avatar
BigDumbDinosaur
Posts: 9425
Joined: 28 May 2009
Location: Midwestern USA (JB Pritzker’s dystopia)
Contact:

Re: Bug in 6502 BRK.

Post by BigDumbDinosaur »

BigEd wrote:
I think the bug is present in all NMOS 6502 cores.
You are correct.  That was one of the things that were corrected in the 65C02, along with the infamous JMP (<addr>) problem.  The 8502 in the Commodore 128 had that bug—I ran into it several times while debugging a program using the resident M/L monitor.
x86?  We ain't got no x86.  We don't NEED no stinking x86!
orac81
Posts: 53
Joined: 20 Apr 2024
Location: uk

Re: Bug in 6502 BRK.

Post by orac81 »

Interesting. So for the various built in or loaded Supermons that do single step with BRK, could these all be corrected by adding some code in the IRQ routine?
I guess with AttackUfo the machine wasnt using the IRQ for anything else so BRK is not too slow used that way?
But in a C64 you have the overhead to split paths for BRK/ IRQ. Also doing a check for a missed BRK, to see if the saved address points to a BRK would make it much slower. Or is that only necessary for the IRQ scan code, which is only called every 1/60th sec?
Has anyone ever actually tried doing this?

Maybe that AttackUfo console uses an IRQ, so it might crash every so often!

Do any early 6502 Forth compilers use this BRK method to save memory?
User avatar
BigDumbDinosaur
Posts: 9425
Joined: 28 May 2009
Location: Midwestern USA (JB Pritzker’s dystopia)
Contact:

Re: Bug in 6502 BRK.

Post by BigDumbDinosaur »

orac81 wrote:
So for the various built in or loaded Supermons that do single step with BRK, could these all be corrected by adding some code in the IRQ routine?
Not in any way I know of—there would be no external indication that a BRK instruction had been “stepped on” by an IRQ.
Quote:
But in a C64 you have the overhead to split paths for BRK/ IRQ.  Also doing a check for a missed BRK, to see if the saved address points to a BRK would make it much slower.
The “split path” code would exist in any 65(C)02 system that utilizes BRK.  A stack sniff followed by a branch is the usual way to differentiate between an IRQ and a software interrupt.  The execution time for BRK in the NMOS MPUs is the same as it is for responding to an IRQ (7 cycles)—the sames steps occur, other than BRK setting the b bit in SR.

The address that was pushed when an IRQ that stepped on a BRK os serviced would be pointing to the BRK opcode’s signature, not the BRK opcode.  When the ISR finished and executed RTI, the MPU would try to execute the signature as though it were an instruction.  Again, there would be no external indication of what had happened.  It is hardware errata.

Quote:
Has anyone ever actually tried doing this?
Ever tried doing what?
Quote:
Do any early 6502 Forth compilers use this BRK method to save memory?
That I wouldn’t know.  I can’t picture any reason why Forth would have used BRK for anything.
x86?  We ain't got no x86.  We don't NEED no stinking x86!
User avatar
BigEd
Posts: 11463
Joined: 11 Dec 2008
Location: England
Contact:

Re: Bug in 6502 BRK.

Post by BigEd »

Pretty easy, if you can afford the cycles, to inspect the byte that might or might not be a BRK, surely?

And in the case of a monitor which intentionally temporarily wrote a BRK as a breakpoint, pretty easy to check the address on the stack?
User avatar
BigDumbDinosaur
Posts: 9425
Joined: 28 May 2009
Location: Midwestern USA (JB Pritzker’s dystopia)
Contact:

Re: Bug in 6502 BRK.

Post by BigDumbDinosaur »

BigEd wrote:
Pretty easy, if you can afford the cycles, to inspect the byte that might or might not be a BRK, surely?

And in the case of a monitor which intentionally temporarily wrote a BRK as a breakpoint, pretty easy to check the address on the stack?
Well, you could fetch the saved address from the stack, subtract one from it and use the result to fetch what you think was the previous opcode.  If BRK has been stepped on by an IRQ, you would fetch $00.  But...

What makes you sure that $00 is a BRK instruction and not an operand, or part of one, to the instruction that was completed right before the IRQ hit?
x86?  We ain't got no x86.  We don't NEED no stinking x86!
User avatar
BigEd
Posts: 11463
Joined: 11 Dec 2008
Location: England
Contact:

Re: Bug in 6502 BRK.

Post by BigEd »

I think you may have something there!
User avatar
BigDumbDinosaur
Posts: 9425
Joined: 28 May 2009
Location: Midwestern USA (JB Pritzker’s dystopia)
Contact:

Re: Bug in 6502 BRK.

Post by BigDumbDinosaur »

BigEd wrote:
I think you may have something there!
I did run into this anomaly a number of times back when I was professionally developing for the C-128.

The 128 has a built-in version of Jim Butterfield’s Supermon and by default, the BRK indirect vector at IBRK ($0316) points to $B003 in ROM, which is an entry point into the monitor.  There were times when I would set a BRK - NOP sequence in code undergoing test so I could look at things.  Every so often, a program would not halt on the BRK because the jiffy IRQ hit at the same instance the 8502 had fetched the BRK opcode.

At the time, I wasn’t aware of this bug, but after a couple of instances of BRKs “malfunctioning,” I decided to dig into the problem.  It was Fred Bowen at Commodore who tipped me off to it.
x86?  We ain't got no x86.  We don't NEED no stinking x86!
dmsc
Posts: 153
Joined: 17 Sep 2018

Re: Bug in 6502 BRK.

Post by dmsc »

Hi!

It is in fact a lot easier to deal with BRK, you just have to see if the flags pushed to the stack has the B bit set.

There is a misunderstanding here about the BRK: the problem is not that the BRK is missed by an IRQ, it is that an NMI can mask a BRK instruction - the CPU jumps to the NMI handler instead of the IRQ handler if the NMI hits at the right spot, but it jump to the NMI with the "B" bit of the flags in the stack.

The above means that you should check for the B flag in the stack in both, the NMI and the IRQ handlers, and in the case of the NMI, you should process the BRK *and* the NMI inside your handler - presumably processing first the NMI and then checking for the "B" flag and jumping to the BRK handler if it is there.

Also, you should always check in your IRQ handler all possible IRQ sources, as it can always happen that multiple IRQ sources activate at the same time. With proper IRQ handling, the level-sensitivity of this makes this easier, as you can simple process one IRQ source and then return, if the IRQ pin is still asserted, the processor will trigger an IRQ again. This is not so in the NMI case, as this line is edge triggered, if you do not handle the interrupt it will be lost.

Have Fun!
dmsc
Posts: 153
Joined: 17 Sep 2018

Re: Bug in 6502 BRK.

Post by dmsc »

Adding to the above, for more details you can see page 45 of the Altirra Hardware manual, at https://www.virtualdub.org/downloads/Al ... Manual.pdf, on "overlapping interrupts", that states:
Quote:
Overlapping interrupts

It is possible for the 6502 to first begin executing the seven-cycle interrupt sequence for an IRQ and then jump to the NMI vector instead if an NMI occurs quickly enough.

For IRQ+NMI conflicts, this behavior simply leads to faster acknowledgment of the NMI. However, it also has unfortunate consequences for the BRK ($00) instruction. The BRK instruction is essentially the same as an IRQ
except that the flags byte pushed on the stack has the B flag set. Because of this, it is possible for an NMI to hijack the BRK sequence in the same way. When this occurs, the NMI vector is invoked with the B flag set on the
flags byte on the stack. Thus, robust handling of BRK instructions requires it to be checked for in both the IRQ and NMI handlers.

There are no issues with an overlapping IRQ and BRK instruction. However, when multiplexing the IRQ vector for both IRQ and BRK, the BRK instruction must be serviced before the handler exits. For multiplexed IRQs, the
handler can service one IRQ at a time, relying on the hardware to keep IRQ asserted as causing the handler to re-execute until all IRQs are serviced. This is not true for BRK, which will be lost if not serviced.

On the Atari, this effect occurs if a BRK instruction begins execution at between cycles 4-8 of a scan line where either the DLI or VBI is activated.
Have Fun!
User avatar
BigDumbDinosaur
Posts: 9425
Joined: 28 May 2009
Location: Midwestern USA (JB Pritzker’s dystopia)
Contact:

Re: Bug in 6502 BRK.

Post by BigDumbDinosaur »

dmsc wrote:
Adding to the above, for more details you can see page 45 of the Altirra Hardware manual, at https://www.virtualdub.org/downloads/Al ... Manual.pdf, on "overlapping interrupts", that states:
Quote:
Overlapping interrupts

...There are no issues with an overlapping IRQ and BRK instruction.
That statement is contradicted in a caveat in the 65C02 data sheet on page 30.  I have run into this quirk of the NMOS 6502 a number of times while doing software development on the Commodore 128.

I can’t vouch for the behavior that occurs when an NMI hits during a BRK opcode fetch because I have never had that sort of condition occur in any software I developed.
x86?  We ain't got no x86.  We don't NEED no stinking x86!
dmsc
Posts: 153
Joined: 17 Sep 2018

Re: Bug in 6502 BRK.

Post by dmsc »

Hi!
BigDumbDinosaur wrote:
dmsc wrote:
Adding to the above, for more details you can see page 45 of the Altirra Hardware manual, at https://www.virtualdub.org/downloads/Al ... Manual.pdf, on "overlapping interrupts", that states:
Quote:
Overlapping interrupts

...There are no issues with an overlapping IRQ and BRK instruction.
That statement is contradicted in a caveat in the 65C02 data sheet on page 30.  I have run into this quirk of the NMOS 6502 a number of times while doing software development on the Commodore 128.

I can’t vouch for the behavior that occurs when an NMI hits during a BRK opcode fetch because I have never had that sort of condition occur in any software I developed.
I have tested BRK a lot in my Atari computer, and the behavior is as described in the Altirra hardware manual - I have never lost a BRK to an IRQ. If you disable the NMI interrupt sources, you can freely use BRK as a reliable instruction.

IMHO, the note on page 30 of the 65C02 manual does not make sense: "Interrupt vector is loaded; BRK vector is ignored.". But IRQ vector is the same as the BRK vector!

The only explanation for the note in the manual is that it actually refers to the NMI interrupt, where the bug is there: the CPU loads the NMI vector instead of the IRQ vector if the NMI happens on the cycle after the BRK is fetched.

Have Fun!
orac81
Posts: 53
Joined: 20 Apr 2024
Location: uk

Re: Bug in 6502 BRK.

Post by orac81 »

It should be possible to test this? If you have a very tight loop around a BRK and a very fast (VIA driven?) IRQ timer running, say on a vic20/c64/c128/pet, you could set up a situation where IRQs and BRKs overlap, and monitor the results by some method.
Last edited by orac81 on Fri Jan 02, 2026 12:05 pm, edited 1 time in total.
jgharston
Posts: 181
Joined: 22 Feb 2004

Re: Bug in 6502 BRK.

Post by jgharston »

I can confirm that it is NMI interupts, not IRQ interupts that can mask a BRK. This came up on the BBC which uses BRK to generate a foreground error, IRQ for background timers and processing, and NMI for file system hardware.

Normally, NMIs would be used for background data transfer while waiting for the data transfer to be completed, so the foreground wouldn't BRK while NMIs were happening as it would wait for the NMI-generating action to complete before continuing and possibly getting to a BRK.

But, there's a situation where in a tight loop making a lot of high level network file server calls to something that often generates errors, that the huge number of network packets flying around could overlap with a BRK generating an error. The typical case is repeatedly trying to open a file which might be open by somebody else, and tightly looping catching the error and repeatedly trying again until you can open it:
ON ERROR keep going
chn%=OPENUP file$
...
the solution here is not to use the high level call that generates an error with BRK when the high level call cannot open the remote file, but use the low level call the OPEN call calls which *returns* the error state rather than *generating* an error
REPEAT
err%=FNnetfs_op(open,update,file$)
UNTIL err%=0

Anyway, you can ignore all that explanation, and just read that: it is NMIs masking BRK on the NMOS 6502, not IRQs.

If you can afford the cycles, the solution - as described upthread - is to check the stacked flags on exiting the NMI handler:
NMIGO:
JSR usernmi
STA irqtmp
PLA:PHA
AND #16:BNE doBRK
LDA irqtmp:RTI
doBRK:
Post Reply