BRK masking bug and reliability of B pseudoflag

Are you new to 6502, NES, or even programming in general? Post any of your questions here. Remember - the only dumb question is the question that remains unasked.

Moderator: Moderators

stan423321
Posts: 31
Joined: Wed Sep 09, 2020 3:08 am

BRK masking bug and reliability of B pseudoflag

Post by stan423321 » Mon Feb 15, 2021 9:57 am

NesDev wiki states that NMI or IRQ that happens during specific moments of BRK execution can cause it to be effectively skipped. The recommended mitigation is to not use BRK in proximity to interrupts, which in many cases means not using it at all.
V6502 wiki page linked from relevant article states that checking B in NMI is not feasible since it's supposed to be fast and possibly called often. There is something said there about when B is set but it appears to just rule out one description.
In context of NES:
  • Any reason checking at the end of NMI wouldn't work? IRQ may not have needed a source check, but that's a more specific scenario than having NMIs on.
  • Is the B pseudoflag reliable enough for such check?
By the way:
  • Which other 6502-like platforms are affected?

User avatar
rainwarrior
Posts: 8001
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: BRK masking bug and reliability of B pseudoflag

Post by rainwarrior » Mon Feb 15, 2021 1:38 pm

stan423321 wrote:
Mon Feb 15, 2021 9:57 am
V6502 wiki page linked from relevant article states that checking B in NMI is not feasible since it's supposed to be fast and possibly called often. There is something said there about when B is set but it appears to just rule out one description.
I guess you're referring to this:
visual6502 wiki wrote: Because the B bit is stored as a 1, even though the NMI vector has been followed, in this case, the NMI handler could inspect the saved P register, in case a BRK was interrupted. It would then have to adjust the saved PC. This all takes time, and yet NMI is usually for rapid interrupt servicing.

As the NMI handler would not normally inspect P, this is a case of NMI masking BRK. If BRK is an OS call, it would not be made, and so you can't do that on a system using NMI.
The description there is really muddled to me. I don't know what it means about "NMI is usually for rapid interrupt servicing", but on the NES you know what you're using the NMI for. I don't think you have to pay attention to that sentence, they're probably thinking about how NMI is used on some other 6502 platform.

As far as I know, checking the pushed B flag at the end of NMI should be sufficient to allow you to correct for a cancelled BRK. I don't see anything on that page that suggests the flag is unreliable?

User avatar
Controllerhead
Posts: 214
Joined: Tue Nov 13, 2018 4:58 am
Location: $4016
Contact:

Re: BRK masking bug and reliability of B pseudoflag

Post by Controllerhead » Mon Feb 15, 2021 1:48 pm

stan423321 wrote:
Mon Feb 15, 2021 9:57 am
NesDev wiki states that NMI or IRQ that happens during specific moments of BRK execution can cause it to be effectively skipped.
Yep. This is a "design quirk" of the 6502. The internal logic of the processor uses the BRK logic to process interrupts, so, if a BRK and an interrupt happen at the same time, the BRK gets eaten: The interrupt vector is only jumped to once and the B flag is not set. Whoops. It is explained here:
https://www.youtube.com/watch?v=fWqBmmPQP40&t=41m44s
stan423321 wrote:
Mon Feb 15, 2021 9:57 am
Which other 6502-like platforms are affected?
All of them that use a 6502. I don't know about the 65c02, 65c816 or other derivative processors.
stan423321 wrote:
Mon Feb 15, 2021 9:57 am
which in many cases means not using it at all.
How so? You have your NMI after all of your game calculation should be done. Some mappers like MMC3 have a scanline interrupt, but that can only be used once per frame, and should be easily anticipated. Sprite 0 isn't an interrupt at all, just a flag that you have to be polling for.

Using it with DPCM could be problematic i suppose: If you enable the DPCM interrupt it could present challenges, and, i don't know if the internal sample fetching logic would cause this BRK skip situation? Above my paygrade.
Last edited by Controllerhead on Mon Feb 15, 2021 3:42 pm, edited 7 times in total.
Image

User avatar
rainwarrior
Posts: 8001
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: BRK masking bug and reliability of B pseudoflag

Post by rainwarrior » Mon Feb 15, 2021 3:14 pm

Controllerhead wrote:
Mon Feb 15, 2021 1:48 pm
Using it with DPCM could be problematic i suppose: If you enable the DPCM interrupt it could present challenges, and, i don't know if the internal sample fetching logic would cause this BRK skip situation? Above my paygrade.
I think the DPCM fetches only interfere with external interface like $2007 and $4016. They shouldn't cause IRQ problems? At least I've never heard of DPCM samples causing a problem for NMI, etc. and I don't believe they affect anything relevant to this situation.

(The DPCM IRQ, if you're using it, should presents only the same problems any other IRQ does.)

lidnariq
Posts: 10248
Joined: Sun Apr 13, 2008 11:12 am
Location: Seattle

Re: BRK masking bug and reliability of B pseudoflag

Post by lidnariq » Mon Feb 15, 2021 3:36 pm

One quick question: What are you hoping to use BRK for?

stan423321
Posts: 31
Joined: Wed Sep 09, 2020 3:08 am

Re: BRK masking bug and reliability of B pseudoflag

Post by stan423321 » Mon Feb 15, 2021 4:45 pm

lidnariq wrote:
Mon Feb 15, 2021 3:36 pm
One quick question: What are you hoping to use BRK for?
I was thinking of the parameter passing routine pattern, if it came to that. So in general, common stuff that can be a little slow if it deflates the code.

Oziphantom
Posts: 1079
Joined: Tue Feb 07, 2017 2:03 am

Re: BRK masking bug and reliability of B pseudoflag

Post by Oziphantom » Mon Feb 15, 2021 10:06 pm

They must be thinking about all the embedded systems that use NMI in the "this is a life saving piece of medical equipment, don't spend another 12 clocks looking up the B flag.

It's not just BRK its any interrupt. If an IRQ interrupts a NMI or an NMI interrupts an IRQ you will get lost interrupts.

unless you are doing DCPM or exotic peripherals you wouldn't even need to worry about checking the B flag. If you are using DCPM read 4015 would be easier, faster and more useful.

User avatar
rainwarrior
Posts: 8001
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: BRK masking bug and reliability of B pseudoflag

Post by rainwarrior » Mon Feb 15, 2021 10:55 pm

Oziphantom wrote:
Mon Feb 15, 2021 10:06 pm
It's not just BRK its any interrupt. If an IRQ interrupts a NMI or an NMI interrupts an IRQ you will get lost interrupts.
When an NMI takes over an IRQ, for most types of IRQ devices it's not going to be an issue because with no acknowledge the IRQ will still be asserting when the NMI handler does RTI.

Can an IRQ actually take over an NMI? I hadn't read about that case, only the other way around... but if this is possible, obviously we can have our update delayed by a frame, but at least for NES purposes it should not cause a crash unless our code cannot handle slowdown / timing variations?

If you are using BRK for some code that must happen (e.g. game logic), it is different than both of those cases. The stolen BRK needs to be detected and recovered from, or whatever the BRK was supposed to do won't happen.
Oziphantom wrote:
Mon Feb 15, 2021 10:06 pm
unless you are doing DCPM or exotic peripherals you wouldn't even need to worry about checking the B flag. If you are using DCPM read 4015 would be easier, faster and more useful.
No, that wouldn't work. If you're using IRQ + BRK, a check for B in the interrupt handler will already do the right thing. The stolen BRK would still do the BRK behaviour first, and the unacknowledged IRQ will still be asserting when it does RTI. If you instead polled the IRQ device, like reading DPCM $4015, the BRK would remain stolen, and only the IRQ would get serviced. The B flag is the only way to know a BRK had been requested, so far as I can tell.

The NMI conflict is the weirder case, but indeed checking B at the end and using it to dispatch the BRK should be sufficient to correct.

User avatar
Controllerhead
Posts: 214
Joined: Tue Nov 13, 2018 4:58 am
Location: $4016
Contact:

Re: BRK masking bug and reliability of B pseudoflag

Post by Controllerhead » Tue Feb 16, 2021 1:49 am

rainwarrior wrote:
Mon Feb 15, 2021 10:55 pm
If you are using BRK for some code that must happen (e.g. game logic)
Hmm... there are no branching instructions for B and no way to set it besides BRK or manually manipulating the status register. To set it with BRK you are both jumping to the interrupt vector and setting the interrupt disable flag, I guess you could like...

Code: Select all

; Nevermind. Ignore this. See below


; Set B
BRK

; Set B Manually
PHP
PLA
ORA #%00010000
PHA
PLP

; Detect B
PHP
PLA
AND #%00010000
BEQ +
  ; break detected. hooray
+

; Clear B
PHP
PLA
AND #%11101111
PHA
PLP

interruptVector:
; You need more logic here if the interrupt vector does other things
CLI
RTI
I can't possibly think of a situation where you would prefer this over using a bit in memory?

EDIT: Nevermind. It doesn't even physically exist.
Image
Last edited by Controllerhead on Tue Feb 16, 2021 2:23 am, edited 2 times in total.
Image

lidnariq
Posts: 10248
Joined: Sun Apr 13, 2008 11:12 am
Location: Seattle

Re: BRK masking bug and reliability of B pseudoflag

Post by lidnariq » Tue Feb 16, 2021 1:55 am

Controllerhead wrote:
Tue Feb 16, 2021 1:49 am
; Detect B
PHP
PLA
AND #%00010000
BEQ +
; break detected. hooray
B isn't a physical register. It only exists when in the stack. PHP always pushes a byte with the "B" bit set.

To detect the B bit, you have to TSX and then lda something,X... I forget what's the right address for "something". Maybe $0101?

User avatar
rainwarrior
Posts: 8001
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: BRK masking bug and reliability of B pseudoflag

Post by rainwarrior » Tue Feb 16, 2021 2:13 am

Yes, the last byte pushed to the stack (P) is $101, X. The second last (PCL) is $102, X, etc...

stan423321
Posts: 31
Joined: Wed Sep 09, 2020 3:08 am

Re: BRK masking bug and reliability of B pseudoflag

Post by stan423321 » Tue Feb 16, 2021 7:32 am

Controllerhead wrote:
Mon Feb 15, 2021 1:48 pm
stan423321 wrote:
Mon Feb 15, 2021 9:57 am
NesDev wiki states that NMI or IRQ that happens during specific moments of BRK execution can cause it to be effectively skipped.
Yep. This is a "design quirk" of the 6502. The internal logic of the processor uses the BRK logic to process interrupts, so, if a BRK and an interrupt happen at the same time, the BRK gets eaten: The interrupt vector is only jumped to once and the B flag is not set. Whoops. It is explained here:
https://www.youtube.com/watch?v=fWqBmmPQP40&t=41m44s
See, that's the thing. That video explains the mechanism behind the takeover happening, but it doesn't seem to say anything about B. It especially doesn't say it's pushed clear like you here, every other account I remember right now at least states it might be set sometimes. V6502 page seems to imply it's always - why go for the NMI speed argument otherwise - but it's already a bit confusing, as rainwarrior stated independently above, I believe.

6502.org's interrupt tutorial states that 65c02 and derivatives should not mask BRKs anymore, so that's something.

Garth
Posts: 206
Joined: Wed Nov 30, 2016 4:45 pm
Location: Southern California
Contact:

Re: BRK masking bug and reliability of B pseudoflag

Post by Garth » Tue Feb 16, 2021 12:24 pm

stan423321 wrote:
Tue Feb 16, 2021 7:32 am
6502.org's interrupt tutorial states that 65c02 and derivatives should not mask BRKs anymore, so that's something.
If you're referring to http://6502.org/tutorials/interrupts.html, the latest is on my own site at http://wilsonminesco.com/6502interrupts/ . I started my own site in 2012 because it was too cumbersome to make updates or post new things on 6502.org. Doing it on my own site is as easy as if it were just a folder on my own hard disc; so my own site will be where the updates get posted. (However, in the case of the 6502 interrupts primer, the differences between the two will be very small.) There are lots of other major 6502 features on my site, linked along the top of that page.
http://WilsonMinesCo.com/ lots of 6502 resources

User avatar
Controllerhead
Posts: 214
Joined: Tue Nov 13, 2018 4:58 am
Location: $4016
Contact:

Re: BRK masking bug and reliability of B pseudoflag

Post by Controllerhead » Tue Feb 16, 2021 1:09 pm

stan423321 wrote:
Tue Feb 16, 2021 7:32 am
That video explains the mechanism behind the takeover happening, but it doesn't say anything about B.
The BRK instruction gets ignored, and as a result, the BRK flag gets ignored.

Image

Look man, just use a bit / byte in memory and save yourself the headaches and the cycles.
Image

stan423321
Posts: 31
Joined: Wed Sep 09, 2020 3:08 am

Re: BRK masking bug and reliability of B pseudoflag

Post by stan423321 » Tue Feb 16, 2021 1:51 pm

Garth wrote:
Tue Feb 16, 2021 12:24 pm
stan423321 wrote:
Tue Feb 16, 2021 7:32 am
6502.org's interrupt tutorial states that 65c02 and derivatives should not mask BRKs anymore, so that's something.
If you're referring to http://6502.org/tutorials/interrupts.html, the latest is on my own site at http://wilsonminesco.com/6502interrupts/ . I started my own site in 2012 because it was too cumbersome to make updates or post new things on 6502.org. Doing it on my own site is as easy as if it were just a folder on my own hard disc; so my own site will be where the updates get posted. (However, in the case of the 6502 interrupts primer, the differences between the two will be very small.) There are lots of other major 6502 features on my site, linked along the top of that page.
Hi! Thanks for the update information. The original version of the article is appreciated as well.
rainwarrior wrote:
Mon Feb 15, 2021 10:55 pm
Can an IRQ actually take over an NMI? I hadn't read about that case, only the other way around... but if this is possible, obviously we can have our update delayed by a frame, but at least for NES purposes it should not cause a crash unless our code cannot handle slowdown / timing variations?
From what I gathered from V6502 wiki - and that thing is not the easiest to read for me, so apologies if it's a misunderstanding - they weren't collectively sure if this can happen in general, but the suspect scenario involves NMI signal being active for a few cycles only. NES NMI line holds for a long while unless you're fiddling with PPUSTATUS, IRQs or not, which is already known to be a problem; otherwise NMI should eventually trigger.
Controllerhead wrote:
Tue Feb 16, 2021 1:09 pm
stan423321 wrote:
Tue Feb 16, 2021 7:32 am
That video explains the mechanism behind the takeover happening, but it doesn't say anything about B.
The BRK instruction gets ignored, and as a result, the BRK flag gets ignored.

Image

Look man, just use a bit / byte in memory and save yourself the headaches and the cycles.

Since you have to take the stack pointer and read $101, x ; you're using memory anyway.
The table you shot seems to simply present a simplification describing the regular behaviour and why some common strings are pulled. The B column is there to differentiate BRK from IRQ. That's how it should be. I'm not sure which one of us is misunderstanding what's being said in this talk, if others could tune in it could help.

There's clearly some difference between other interrupts and BRK, as BRK adds to the stored return address. At least that part is preserved in case of a masked BRK; that's what causes the masking. Otherwise, RTI would fall onto the BRK and there would be no problem. If BRK would keep the return address pointing at itself there would also be no problem.

If we were in a rush to release a commercial game, I would agree that trying to figure this out for that very game is probably not worth the time. I can assure you that's not the case.

An important part about the "bit in the memory" is that it can be a relative hindrance to check B, but one place in the ROM is likely enough. Setting up anything else takes space which would probably be better "wasted" on a whole BSR instead.

Post Reply