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

User avatar
Controllerhead
Posts: 218
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 2:07 pm

stan423321 wrote:
Tue Feb 16, 2021 1:51 pm
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.
Once you RTI from your BRK the BRK is gone... So you would have to run all of your code in the interrupt vector? Or conditionally jump out of it? And if you used JSR you wouldn't be able to check for it... I just... There are tons of better ways to store and check bit. I don't know man. You do you. Good luck.
Image

User avatar
rainwarrior
Posts: 8006
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:12 pm

Controllerhead wrote:
Tue Feb 16, 2021 1:09 pm
Look man, just use a bit / byte in memory and save yourself the headaches and the cycles.
I think the goal with using BRK is usually to save space, not cycles? All else considered, it's a fantastically concise way to dispatch some kinds of things.

The stack manipulation sounds fussy to work with but you only write that part once, and then you use the BRK a hundred times.

Treating it as a 2-byte instruction with 1 parameter, it can be a little bit like the x86 INT instruction, where you have a set of common "system" routines selected via the parameter byte.

Another idiom that I like that can produce very concise code:

Code: Select all

jsr function
.byte param1, param2, param3, param4

function:
	; read return address from stack and use it to access parameters
	; do function
	; add parameter data size to the return address
	rts

; macros can make it very convenient too:

.macro FUNCTION p1, p2, p3, p4
	jsr function
	.byte p1, p2, p3, p4
.endmacro

; everything on one convenient line
FUNCTION 1 2 3 4

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

Re: BRK masking bug and reliability of B pseudoflag

Post by Oziphantom » Tue Feb 16, 2021 10:26 pm

So the official way to do it is ; <from the Commodore KERNALs>

Code: Select all

.C:ff48  48          PHA
.C:ff49  8A          TXA
.C:ff4a  48          PHA
.C:ff4b  98          TYA
.C:ff4c  48          PHA
.C:ff4d  BA          TSX
.C:ff4e  BD 04 01    LDA $0104,X
.C:ff51  29 10       AND #$10
.C:ff53  F0 03       BEQ $FF58
.C:ff55  6C 16 03    JMP ($0316) ; BRK here
.C:ff58  6C 14 03    JMP ($0314) ; IRQ here
rainwarrior wrote:
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?
Yes in my Duck Hunt port I had to lock my sample frequency to be a perfect multiple of the clocks per line to ensure the IRQ didn't fire over my NMI and cause my NMI to be lost. If they got dropped the whole game would implode on it self ;) What would be interesting to know is if you can use an NMI/IRQ to abort a RESET. Not really sure how you would set it up possibly some custom hardware on say a Expansion Port that drops RESET then counts 2 Phi 2 clocks and drops NMI and see what happens?
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.
Well yes for detecting the "BRK has been aborted" 4015 is not useful. however it is useful for knowing that one has happened and you do probably need to handle it reasonably fast, i.e at least first to keep the DCPM pumping. Then after servicing the DCPM, check to see if B exists and deal with it accordingly. Although it is not clear to me that the B flag will always be set properly if the BRK is aborted by the IRQ or NMI hitting in the first 2 clocks before the push happens. As it can't restart the sequence nor can it wait for the sequence to finish otherwise you get a spare address and status flags on the stack so when you next to an RTS you're going to crash. So it must steal the sequence to keep the Stack consistent.

To which I propose the only truly safe measure is
fetch return address from stack
-2
fetch byte and check to see if it is 0
if it is, jmp to BRK handler

Although possibly one could just risk it given you know you don't have DCPM and your game is in "one frame" so you won't have an NMI overlap issue. It depends on the project at hand.

Some assemblers back in the day use to come with BRK based instruction extensions libraries built in.

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

Re: BRK masking bug and reliability of B pseudoflag

Post by rainwarrior » Wed Feb 17, 2021 12:07 am

Oziphantom wrote:
Tue Feb 16, 2021 10:26 pm
Well yes for detecting the "BRK has been aborted" 4015 is not useful. however it is useful for knowing that one has happened and you do probably need to handle it reasonably fast, i.e at least first to keep the DCPM pumping. Then after servicing the DCPM, check to see if B exists and deal with it accordingly.
Sure, checking the IRQ status first before checking B would let you do a more timely IRQ dispatch. I interpreted what you said as suggesting to check it instead of B, which seemed contrary to the OP's question which is really about whether BRK can be serviced reliably. I was mostly thinking that the "naive" case of only checking B would still service both the BRK and the IRQ, but making sure to handle the IRQ first is a good suggestion if your IRQ is timing sensitive.
Oziphantom wrote:
Tue Feb 16, 2021 10:26 pm
To which I propose the only truly safe measure is
fetch return address from stack
-2
fetch byte and check to see if it is 0
if it is, jmp to BRK handler
I'm confused by what this is suggesting to do? Why would you take the return address from the stack? To what are you doing "-2"? What are you checking if is 0? (The B flag is set for a BRK...?)

The C64 kernal snippet you posted looks like a very good example of how to read the B flag and branch to your BRK handler, and it would fine to do the same process instead at the end of an IRQ or NMI to catch the interrupted BRK.
Oziphantom wrote:
Tue Feb 16, 2021 10:26 pm
Although it is not clear to me that the B flag will always be set properly if the BRK is aborted by the IRQ or NMI hitting in the first 2 clocks before the push happens.
The Visual6502 article and our Wiki article only mention cases where the NMI interrupting a BRK has the B flag set. Our wiki cites as reference blargg's cpu_interrupts_v2 test, which documents that the B flag is set in the NMI handler when a BRK is interrupted at 5 different timing alignments. My belief was that it's reliable for an interrupted BRK, based on this.

It might be worth writing a different hardware test to verify its reliability with another means though. The behaviour should be easy to demonstrate. I haven't personally verified it, as I've only actually used BRK for this system-call type of behaviour on Apple II, where the NMI isn't really used normally.
Last edited by rainwarrior on Wed Feb 17, 2021 1:10 am, edited 3 times in total.

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

Re: BRK masking bug and reliability of B pseudoflag

Post by Oziphantom » Wed Feb 17, 2021 1:02 am

rainwarrior wrote:
Wed Feb 17, 2021 12:07 am
Oziphantom wrote:
Tue Feb 16, 2021 10:26 pm
To which I propose the only truly safe measure is
fetch return address from stack
-2
fetch byte and check to see if it is 0
if it is, jmp to BRK handler
I'm confused by what this is suggesting to do? Why would you take the return address from the stack? To what are you doing "-2"? What are you checking if is 0? (The B flag is set for a BRK...?)

The C64 kernal snippet you posted looks like a very good example of how to read the B flag and branch to your BRK handler, and it would fine to do the same process instead at the end of an IRQ or NMI to catch the interrupted BRK.
My idea was, look up the byte 2 before and see that it is 00. i.e check for a BRK XX XX command. Now that I'm more awake, there are some cases were you could end up with a 00 2 before that was not actually a BRK and thus this is not fool proof either.
rainwarrior wrote:
Wed Feb 17, 2021 12:07 am
Oziphantom wrote:
Tue Feb 16, 2021 10:26 pm
Although it is not clear to me that the B flag will always be set properly if the BRK is aborted by the IRQ or NMI hitting in the first 2 clocks before the push happens.
The Visual6502 article and our Wiki article only mention cases where the NMI interrupting a BRK has the B flag set. Our wiki cites as reference blargg's cpu_interrupts_v2 test, which documents that the B flag is set in the NMI handler when a BRK is interrupted at 5 different timing alignments. My belief was that it's reliable for an interrupted BRK, based on this.

It might be worth writing a different hardware test to verify its reliability with another means though. The behavior should be easy to demonstrate. I haven't personally verified it, as I've only actually used BRK for this system-call type of behaviour on Apple II, where the NMI isn't really used normally.
If the B flag as suggest by the article ( the link is broken by the way, it needs a www. at the start it currently get tagged onto the forums address as a relative path ) is dependable then my above idea is moot ;) And it is nice to have it solidly confirmed as safe. I've been tempted to the BRK dispatch in the past but have been afraid too.

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

Re: BRK masking bug and reliability of B pseudoflag

Post by rainwarrior » Wed Feb 17, 2021 1:16 am

Oziphantom wrote:
Wed Feb 17, 2021 1:02 am
My idea was, look up the byte 2 before and see that it is 00. i.e check for a BRK XX XX command. Now that I'm more awake, there are some cases were you could end up with a 00 2 before that was not actually a BRK and thus this is not fool proof either.
Ohhh, I understand now. Hmm, yeah I can't think of a way to identify what instruction was interrupted like that.

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

Re: BRK masking bug and reliability of B pseudoflag

Post by stan423321 » Wed Feb 17, 2021 3:13 am

It can be technically done, but without some help data in the code it would probably have to be extremely slow. And the whole point here is to save two bytes per call compared to JSRing into the dispatch call, so help data seems counterproductive.

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

Re: BRK masking bug and reliability of B pseudoflag

Post by Oziphantom » Wed Feb 17, 2021 3:34 am

well it gives you a single dispatch point, so you could for example put all the "handle" code in another bank, then call this from any bank and since it goes via a vector it gives you a the ability to trap and dispatch as needed. It also frees up registers.

ldy #what I want you to do
lda #param1
ldx #param2
jsr fixedAddress

becomes
lda #param2
ldx #param3
ldy #param4
BRK WhatIWantYouToDo param1

so you can encode the operation and 1 param or upto 65536 operations in the instruction with a single dispatch handler

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

Re: BRK masking bug and reliability of B pseudoflag

Post by stan423321 » Wed Feb 17, 2021 5:25 am

Oh, banking is a really good point to consider. I guess that saves more ROM than I was thinking.

Still, if we needed, say, two more bytes per call to make BRKs easy to detect, it'd be saner to just jump to the dispatcher directly. So it boils down to the B pseudoflag reliability again.

I'm really interested about the case of NMI being masked. It would of course be advisable to keep IRQ out of NMI's way regardless, but I'm really surprised it would mask it outright. It's supposed to be non-maskable after all. Do you remember any details?

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

Re: BRK masking bug and reliability of B pseudoflag

Post by stan423321 » Wed Feb 17, 2021 8:47 am

Pardon the double post. I may have figured out a workaround in case B doesn't reliably work that only has the JSR cost if there's no post-BRK payload whatsoever, and otherwise can be reduced to a byte, five bits, or further. The trick is to put an otherwise unused opcode as the RTI target. KIL/HLT/JAM/STP seem rather useless to return to, and there's a group of eight of them that shares five bits, so they would be comparatively easy to check for while allowing to distinguish them as well. Different schemes are possible, of course, though there aren't that many completely useless opcodes, but there're a few redundant ones.

Of course, there's more indirection involved, so B would be preferable. Maybe HLT trick (pun entirely intended) would make a test ROM easier? Probably not, that can just index the test BRKs. Oh well.

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

Re: BRK masking bug and reliability of B pseudoflag

Post by Oziphantom » Wed Feb 17, 2021 9:05 am

It was two CIA timers, one IRQ and one NMI. NMI runs the main splits, the IRQ runs the Sample playback. It was on VICE so it could be that VICE allows the NMI to be aborted as well, which the hardware might not. But VICE tends to have done a lot of research into these things, or find the edge case because some demo coders had it happen to them etc.

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

Re: BRK masking bug and reliability of B pseudoflag

Post by stan423321 » Wed Feb 17, 2021 9:41 am

Ah, okay, should have figured out that wasn't NES you would port Duck Hunt to. I can't say if the VICE is correct about this, but it's less confusing now.

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

Re: BRK masking bug and reliability of B pseudoflag

Post by rainwarrior » Wed Feb 17, 2021 12:14 pm

stan423321 wrote:
Wed Feb 17, 2021 8:47 am
Pardon the double post. I may have figured out a workaround in case B doesn't reliably work that only has the JSR cost if there's no post-BRK payload whatsoever, and otherwise can be reduced to a byte, five bits, or further. The trick is to put an otherwise unused opcode as the RTI target. KIL/HLT/JAM/STP seem rather useless to return to, and there's a group of eight of them that shares five bits, so they would be comparatively easy to check for while allowing to distinguish them as well. Different schemes are possible, of course, though there aren't that many completely useless opcodes, but there're a few redundant ones.
The problem is that after an interrupt you have no idea about the size of the interrupted instruction. How do we tell a 16-bit operand apart from an opcode? E.g.:

Code: Select all

$8D $00 $80: STA $8000
    $00 $80: BRK, .byte $80
I think Blargg's test is good reason to assume B is reliable though? I might write a different test for it on the weekend.

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

Re: BRK masking bug and reliability of B pseudoflag

Post by stan423321 » Wed Feb 17, 2021 1:25 pm

rainwarrior wrote:
Wed Feb 17, 2021 12:14 pm
The problem is that after an interrupt you have no idea about the size of the interrupted instruction. How do we tell a 16-bit operand apart from an opcode? E.g.:

Code: Select all

$8D $00 $80: STA $8000
    $00 $80: BRK, .byte $80
I think Blargg's test is good reason to assume B is reliable though? I might write a different test for it on the weekend.
It's likely B works, sure, although more testing would help, but if it doesn't, what I meant is that we can do this:

Code: Select all

BRK
.byte $80
.byte $12
because we can outright "ban" this since it makes no sense:

Code: Select all

STA $8000
.byte $12

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

Re: BRK masking bug and reliability of B pseudoflag

Post by rainwarrior » Wed Feb 17, 2021 5:10 pm

Oh, I see. Since we can't really know how many bytes the interrupted instruction was, but we can know what the next opcode is, you're suggesting placing another marker byte after a BRK...

That seems like a valid way to mark it, though it's starting to hurt BRK's main use of being compact. It's about half a byte away from being worse than just doing a JSR... :S (For x86 INT style system routine selection I think JSR is already better than a 3-byte BRK.)

Post Reply