| |
Trap
Registered: Jul 2010 Posts: 221 |
Best practice IRQ recovery
Hi,
Here's a little newbie question. Sorry, I'm still learning this shit and it's really complicated :(
I have kernel off ($01=$35) and I am running IRQ's using the normal $fffe/$ffff vectors.
I want to exit from this and call a prepacked piece of code (in this case something packed with TinyCrunch).
I tried restoring the IRQ vectors and jump to the packer. However, it just hangs. I tried some other things but all gave the same result. The only thing that worked was when I did this:
sei
lda #$36
sta $01
jsr $ff81
jmp unpacker
The problem of course is that it resets the VIC which isn't really great for my situation.
So, my question:
What is the correct/proper way to exit from a part and go to the next? preferably not using kernal routines :|
Thank you.
Trap |
|
| |
TheRyk
Registered: Mar 2009 Posts: 2035 |
F...ing around with $01 reg just to use KERNAL routines is hardly ever worth the trouble if you don't want KERNAL banked in other parts of your code.
Just look into KERNAL ROM routines to see what they do, for example here: http://unusedino.de/ec64/technical/aay/c64/krnromma.htm and then take the few things you need, normally that would be what you find at $EA81 = restoring accumulator and x/y registers and use them somewhere in RAM. |
| |
Six
Registered: Apr 2002 Posts: 287 |
Where does it hang, before or after the depacker starts? |
| |
chatGPZ
Registered: Dec 2001 Posts: 11088 |
Quote:What is the correct/proper way to exit from a part and go to the next?
that raises the question what you are doing there... a trackmo? classic "press space" stuff? |
| |
Trap
Registered: Jul 2010 Posts: 221 |
It crashes before it gets to the unpacking |
| |
MagerValp
Registered: Dec 2001 Posts: 1055 |
Set a breakpoint at unpacker and then step through the code and see where it crashes? There's no one answer here, it makes some assumption about things and you'll have to figure out what it is. |
| |
chatGPZ
Registered: Dec 2001 Posts: 11088 |
the first thing that comes to mind is... don't use SEI. disable the irq sources instead. then set the pointers to their defaults. |
| |
Krill
Registered: Apr 2002 Posts: 2804 |
Without further information, my bet is on pending interrupts that wreak havoc as soon as the I flag is cleared. =)
Then wrong vectors/memory configuration usually cause crashes, or with the ROM interrupt handler restored, an infinite IRQ-handling loop (as the ROM interrupt handler only acknowledges CIA1 interrupts, but not VIC interrupts).
Bottom line being that properly disabling any interrupt sources (not just executing SEI) is good practice for cleanly exiting a demo part (in a classical spacemo, that is).
Edit: Oh yeah, what Groepaz said. :D |
| |
Trap
Registered: Jul 2010 Posts: 221 |
Well, I'm just learning stuff right now :)
ok, I will re-visit setting the IRQ vectors back. What would be the perfect sequence for this?
Should I just modify the running IRQ code to go to the default IRQ handler and then wait a few frames for it to catch it? |
| |
Krill
Registered: Apr 2002 Posts: 2804 |
It's common practice to check for space during an interrupt handler that's executed once a video frame. The I flag is usually still set (because you're in an interrupt) and the interrupt itself was triggered by VIC.
When detecting the keypress, just write 0 to $d01a to disable any VIC interrupts, then write $ff to $d019 to acknowledge any pending VIC interrupts. Resetting any interrupt vectors should not be required. Directly exit to whatever code to unpack and run the next part. |
| |
Trap
Registered: Jul 2010 Posts: 221 |
So
set IRQ vector to default handler
$d01a=0
$d019=$ff
wait a frame or two
go to unpacker
Is that what you meant? |
| |
Krill
Registered: Apr 2002 Posts: 2804 |
Resetting IRQ vectors or waiting shouldn't be required, but should be no harm. It's just unnecessary. :) |
| |
Oswald
Registered: Apr 2002 Posts: 5007 |
my guess is that depacker overwrites your irq code, and on the next irq request cpu tries to jump into that. simply set irq vector to a new piece of irq handler which surely is undisturbed. |
| |
Flavioweb
Registered: Nov 2011 Posts: 442 |
I guess something overwrite $0314/5 before setting $01 to $36.
Force it back to $EA31 (or $EA81) (and may be better to restore other vectors too) insted of call $FF81.
Clear VIC IRQs enable flags too. |
| |
TWW
Registered: Jul 2009 Posts: 541 |
In your IRQ setup, disable CIA Interrupts and ack any pending before setting up your VIC IRQ and IRQ pointers.
Assuming no NMI's, the only thing which should be able to trigger the IRQ handler is the VIC.
If your keyboard detection routine is inside an IRQ, the I-Flag should remain set until you ack it. This means that as long as you don't ack it and the unpacker doesn't ack/cli no IRQ should trigger before your next part start (where you presumably SEI and setup next round of IRQ interrupts). |
| |
ChristopherJam
Registered: Aug 2004 Posts: 1359 |
If you disable CIA and VIC interrupts *without* a preceding SEI you don't even need to acknowledge any pending ones - the existing handler will deal with that.
tinycrunch's self extracting mode only wraps the decrunch in SEI/CLI because too many people were having issues with nucrunch's default of just turning off CIA while trying to crunch things that needed the kernal interrupt to still be in place when they started up :-/ |
| |
chatGPZ
Registered: Dec 2001 Posts: 11088 |
someone gotta write this "SEI considered harmful" rant already :) |
| |
ChristopherJam
Registered: Aug 2004 Posts: 1359 |
Quote: someone gotta write this "SEI considered harmful" rant already :)
Well I think it's already been flogged to death over at
TIL: The instruction after SEI can be executed before a pending IRQ is handled and to a lesser extent in C-64 coding cargo cults but it's always been as an offshoot from a broader discussion, so far as I can find. |
| |
Martin Piper
Registered: Nov 2007 Posts: 629 |
Tried single stepping using the debugger in Vice?
It will probably show where various IRQs happen after the CLI.
Create, or reuse, consistent IRQ disable and ACK code: https://github.com/martinpiper/C64Public/blob/master/stdlib/std..
I generally always use this to initialise to a known state: https://github.com/martinpiper/C64Public/blob/master/stdlib/Ini.. |
| |
Oswald
Registered: Apr 2002 Posts: 5007 |
"lda #$7f
sta CIA1InterruptControl
sta CIA2InterruptControl"
I will stick to dc0d dd0d :D letsnamealltheregisters_so_itseasyertocode NOT. |
| |
Martin Piper
Registered: Nov 2007 Posts: 629 |
It's easier for me to name registers and sometimes their bitfields. |
| |
chatGPZ
Registered: Dec 2001 Posts: 11088 |
Is this "how to make trivial code unreadable 101"? /o\ |
| |
Krill
Registered: Apr 2002 Posts: 2804 |
Oh come on, the registers have names already in the datasheet.
And not everybody spent years writing and analysing code with a memory monitor before using a proper assembler. =)
I'd stick to concise symbols like "CIA1_ICR", though.
And yes, using symbols instead of raw addresses and bitfields can make code more readable, especially with rarely used registers. |
| |
Oswald
Registered: Apr 2002 Posts: 5007 |
Quote: Oh come on, the registers have names already in the datasheet.
And not everybody spent years writing and analysing code with a memory monitor before using a proper assembler. =)
I'd stick to concise symbols like "CIA1_ICR", though.
And yes, using symbols instead of raw addresses and bitfields can make code more readable, especially with rarely used registers.
I'm sorry, for me lda $#7f sta $dc0d is more readable than lda #CIAdisableallinterrupts sta Cia2interruptcontrolregister. anyone who didnt spent years using these registers by hex values raise their hands. nobody? good :)
and btw if you code like this, and then you see your code in monitor, you will have no clue what part of your code you see. same goes for source code thats programmed as a script. |
| |
TWW
Registered: Jul 2009 Posts: 541 |
Best of both worlds :)
// Disable all CIA interrupts
lda #$7f
sta $dc0d
sta $dd0d
// Acknowlege any pending CIA iterrupts
lda $dc0d
lda $dd0d
|
| |
Krill
Registered: Apr 2002 Posts: 2804 |
Quoting Oswaldanyone who didnt spent years using these registers by hex values raise their hands. nobody? good :) Some people got into C-64 coding only recently.
Quote:if you code like this, and then you see your code in monitor, you will have no clue what part of your code you see. VICEmon symbol files have been invented. \=D/
Quote:same goes for source code thats programmed as a script. Please elaborate. |
| |
chatGPZ
Registered: Dec 2001 Posts: 11088 |
TWW: there are no pending interrupts after disabling them (or to be precise, they'd still trigger, before you can even ACK, the original handler will take care of them) |
| |
TWW
Registered: Jul 2009 Posts: 541 |
GPZ: This is done inside a sei-cli raster IRQ setup to ensure no accidental CIA IRQ triggers after cli'ing which may have activated between the sei and the point you actually disable them. |
| |
chatGPZ
Registered: Dec 2001 Posts: 11088 |
so DONT USE SEI/CLI. *sigh* |
| |
TWW
Registered: Jul 2009 Posts: 541 |
This was posted in response to the discussion above regarding $dc0d vs CIA1_REG whatever above by Oswald/Krill.
*sight* yourself... |
| |
chatGPZ
Registered: Dec 2001 Posts: 11088 |
>_< |
| |
Frantic
Registered: Mar 2003 Posts: 1626 |
GPZ: How about writing that SEI/CLI rant at https://codebase64.org/doku.php?id=base:interrupts ? :D |
| |
chatGPZ
Registered: Dec 2001 Posts: 11088 |
Should do that, some day. Better yet, trick cjam into doing it :o) |
| |
Frantic
Registered: Mar 2003 Posts: 1626 |
@CJam: Groepaz said you had promised to write that anti SEI/CLI rant at https://codebase64.org/doku.php?id=base:interrupts We are waiting. |
| |
Oswald
Registered: Apr 2002 Posts: 5007 |
Quote: Best of both worlds :)
// Disable all CIA interrupts
lda #$7f
sta $dc0d
sta $dd0d
// Acknowlege any pending CIA iterrupts
lda $dc0d
lda $dd0d
winner :) |
| |
Oswald
Registered: Apr 2002 Posts: 5007 |
Krill, too much macroing / kickassing when people write a code that spits out sourcecode which gets assembled. and often we get questions here, which shows the guy thinks he writes the code that runs on c64, and not a code that generates src :D anyway, there's no way good of doing it just different tastes, and I like to voice my dislike for some of these. My other point against script src generation, is that often staring at your code in monitor (without label resolving!) gives the best ideas for optimising it, hence finally you see it at "matrix level", and not the assembler shit which is few steps away from what the cpu really sees. Either you have to be the cpu for good optimisation, so see the problem from an extremely low level, my other excersize to finding faster ways is to try to find a simpler solution to solve the problem. NOT always but 10 times out of 9 it will be faster.
the downside is that these two most of time doesnt gives you a better algoritmic solution, just good to shave off a few instructions.
ahyeah I also used to think stuff like "okay how to end up with the same result without having to do this or that" that mostly end up in crazy table shit, and not much win. |
| |
TWW
Registered: Jul 2009 Posts: 541 |
Quoting OswaldMy other point against script src generation, is that often staring at your code in monitor (without label resolving!) gives the best ideas for optimising it
This is a good point. I often catch myself doing this especially since I perhaps rely a little much on scripts/macros. If I really need to get down and dirty, the monitor is where it's at. |
| |
Krill
Registered: Apr 2002 Posts: 2804 |
Quoting OswaldKrill, too much macroing / kickassing when people write a code that spits out sourcecode which gets assembled. and often we get questions here, which shows the guy thinks he writes the code that runs on c64, and not a code that generates src :D I dunno, seems like just overusing the hammer that is Kickass scripting and wanting to make everything a nail. :) Scripting, offline code generation and external tools producing data to .incbin are all good things, when used right.
Quoting OswaldMy other point against script src generation, is that often staring at your code in monitor (without label resolving!) gives the best ideas for optimising it Source level and machine code level are two different things. I also like to look at the code in the monitor to spot low-level (non-algorithmic) performance or size optimisation opportunities, but translating the solutions back to the source code then results in a higher-level representation to produce the desired output. |
| |
Copyfault
Registered: Dec 2001 Posts: 466 |
Quoting TWWBest of both worlds :)
// Disable all CIA interrupts
lda #$7f
sta $dc0d
sta $dd0d
// Acknowlege any pending CIA iterrupts
lda $dc0d
lda $dd0d
What about this ;)
ldx #$80
nop $dc8d,x
dex
stx $dc0d
stx $dd0d
Then again, Groepaz is right: once an irq-source is disabled, acknowledging should be in the realm of the irq handler.
And Frantic is even more correct in stating that the SEI considered harmful-article has to be added to codebase asap'st ;) |
| |
Copyfault
Registered: Dec 2001 Posts: 466 |
Quoting Krill[...]
Quoting OswaldMy other point against script src generation, is that often staring at your code in monitor (without label resolving!) gives the best ideas for optimising it Source level and machine code level are two different things. I also like to look at the code in the monitor to spot low-level (non-algorithmic) performance or size optimisation opportunities, but translating the solutions back to the source code then results in a higher-level representation to produce the desired output. Exactly what I thought when reading all the comments on "Kickass-scripting". I think you, Oswald, are totally right in stating that most (if not all) golden optimization nuggets will solely be found when looking at the code on a monitor-level (be it via Vice, C64Debugger, on the real deal, whatnot).
But expecially when it comes to data preparation (which I think is the main purpose for the nowadays oh so popular "scripting"), I'm not willing to do this manually nor write everything as 6510-native-code if it can be avoided. But maybe I'm just getting old and lazy, idk... |
| |
chatGPZ
Registered: Dec 2001 Posts: 11088 |
Quote:And Frantic is even more correct in stating that the SEI considered harmful-article has to be added to codebase asap'st ;)
another volunteer! |
| |
TWW
Registered: Jul 2009 Posts: 541 |
Quoting Copyfaultacknowledging should be in the realm of the irq handler.
Even if you disable new CIA IRQ's from occurring, existing ones isn't cleared and may trigger a premature interrupt.
Simply put:
// You do not know the state of the CIA here
sei
// a CIA interrupt may activate here
lda #$7f
sta $dc0d
sta $dc0d
// CIA maintain the IRQ signal, but don't generate new ones
lda #100
sta $d012 // This is where you want the IRQ to happen
lda #1
sta $d01a
fffe/ffff vetor = IRQ_Code
cli
// latent CIA IRQ triggers here instead of @ line 100
jmp *
IRQ_Code:
blablabla
Avoided by ack'ing the CIAs prior to cli'ing |
| |
chatGPZ
Registered: Dec 2001 Posts: 11088 |
we *really* need this rant :)
// switch off irq sources
lda #$7f
sta $dc0d
sta $dd0d
ldx #0
stx $d01a
// whatever irq was pending triggers here and the old handler handles it
// setup irq sources and change pointers
lda #100
sta $d012
lda #$1b
sta $d011
lda #>handler
sta $ffff
lda #<handler
sta $fffe
// perhaps wait for rasterline here to avoid glitching
// enable irq sources
inx
stx $d01a
|
| |
Copyfault
Registered: Dec 2001 Posts: 466 |
Quoting Groepazwe *really* need this rant :) We do, but I won't volunteer as I'm not worthy enough to tickle this sensitive topic!
Quoting Groepaz
// switch off irq sources
lda #$7f
sta $dc0d
sta $dd0d
ldx #0
stx $d01a
// whatever irq was pending triggers here and the old handler handles it
// setup irq sources and change pointers
lda #100
sta $d012
lda #$1b
sta $d011
lda #>handler
sta $ffff
lda #<handler
sta $fffe
// perhaps wait for rasterline here to avoid glitching
// enable irq sources
inx
stx $d01a
Yes, this is the way to do it. Only detail I'm not sure about is when the potentially still pending IRQs might happen: my guess is that the irq acknowledging will still be in place, i.e. any "old" irq routine might be called at different spots.
// switch off irq sources
lda #$7f
sta $dc0d
//if an irq condition happens before cycle 3 of this opcode, it will be
//triggered directly after the STA-opcode has been processed...
sta $dd0d
//...if not, it will take until this 2nd STA-opcode before the irq is triggered
ldx #0
//same here: any pending IRQs of CIA2 will happen here the latest
stx $d01a
// whatever irq was pending triggers here...
// setup irq sources and change pointers
lda #100
//..or here if the raster IRQ condition occured at cycle 3 or 4 of the STX-opcode
[...]
Is this correct or do I mix things up? |
| |
Martin Piper
Registered: Nov 2007 Posts: 629 |
If there was code running that used two or more interrupt sources to operate correctly, then using SEI/CLI would be advisable. This is because the code below without SEI/CLI is not atomic:
lda #$7f
sta $dc0d
sta $dd0d
ldx #0
stx $d01a
In other words, there are a few cycles between switching off IRQ source from CIA1, CIA2 and VIC2 where such complex code could misbehave, say for example by enabling the source in the IRQ that you've just switched off.
So using SEI/CLI is the correct bullet proof way of tackling that issue. |
| |
TWW
Registered: Jul 2009 Posts: 541 |
Quote: If there was code running that used two or more interrupt sources to operate correctly, then using SEI/CLI would be advisable. This is because the code below without SEI/CLI is not atomic:
lda #$7f
sta $dc0d
sta $dd0d
ldx #0
stx $d01a
In other words, there are a few cycles between switching off IRQ source from CIA1, CIA2 and VIC2 where such complex code could misbehave, say for example by enabling the source in the IRQ that you've just switched off.
So using SEI/CLI is the correct bullet proof way of tackling that issue.
Yes!
// Unknown state of VIC/CIA
sei
ldx #0
stx $d01a
lda #$7f
sta $dc0d
sta $dd0d
cli
// IRQ May happen here if one was triggered between the SEI and the write to the IRQ reg to diasble them.
// Unknown state of VIC/CIA
sei
lda #0
sta $d01a
lda #$7f
sta $dc0d
sta $dd0d
lda $dc0d
lda $dd0d
lda #1
sta $d019
cli
// No URQ can trigger now from VIC/CIA.
@ GPZ: "// perhaps wait for rasterline here to avoid glitching"
Instead, just ack any pending IRQ's and you don't have to wait. Don't overthink it.
Edit: Typos |
| |
ChristopherJam
Registered: Aug 2004 Posts: 1359 |
so, the following would work, but it's hella overengineered.
Quoting TWWYes!
// Unknown state of VIC/CIA
sei
ldx #0
stx $d01a
lda #$7f
sta $dc0d
sta $dd0d
cli
// IRQ May happen here if one was triggered between the SEI and the write to the IRQ reg to diasble them.
Given that you're still allowing the triggered IRQ to go ahead, the only effect of wrapping the above in SEI/CLI is that you're potentially delaying the response to the IRQ to after
the write, hence giving the IRQ handler an opportunity to undo it.
Quote:
// Unknown state of VIC/CIA
sei
lda #0
sta $d01a
lda #$7f
sta $dc0d
sta $dd0d
lda $dc0d
lda $dd0d
lda #1
sta $d019
cli
// No URQ can trigger now from VIC/CIA.
^the entire sequence of re-disabling CIA interrupts and acknowledging any that occured is only required in this section because you wrapped the first section in SEI/CLI |
| |
ChristopherJam
Registered: Aug 2004 Posts: 1359 |
Quote: If there was code running that used two or more interrupt sources to operate correctly, then using SEI/CLI would be advisable. This is because the code below without SEI/CLI is not atomic:
lda #$7f
sta $dc0d
sta $dd0d
ldx #0
stx $d01a
In other words, there are a few cycles between switching off IRQ source from CIA1, CIA2 and VIC2 where such complex code could misbehave, say for example by enabling the source in the IRQ that you've just switched off.
So using SEI/CLI is the correct bullet proof way of tackling that issue.
The sequence doesn't need to be atomic.
Interrupts could occur before you enter the sequence regardless of whether the sequence itself is wrapped, and leaving the I flag clear just allows them to occur as late as just before the instruction performing the write.
Once the write occurs however, as long as you *didn't* execute an SEI, no CIA interrupts will occur afterwards.
And if you're concerned about a VIC interrupt reenabling your CIA interrupt, then wrapping that your code with SEI/CLI will only defer that to the end of the section; it does nothing to protect you against that. |
| |
ChristopherJam
Registered: Aug 2004 Posts: 1359 |
argh, damn it. Give the possibility of "some other coder's VIC interrupt setting up a CIA interrupt or vise versa" I might actually need to reconsider my stance.
How hostile an environment do I want to guard against is the question I guess.. |
| |
Martin Piper
Registered: Nov 2007 Posts: 629 |
For the cost of two bytes SEI/CLI pair to handle all the hostile cases, I'd say it was reasonable.
I might even switch of the CIA2 source first, then CIA1. To handle NMIs. |
| |
TWW
Registered: Jul 2009 Posts: 541 |
Quote: For the cost of two bytes SEI/CLI pair to handle all the hostile cases, I'd say it was reasonable.
I might even switch of the CIA2 source first, then CIA1. To handle NMIs.
Better yet, ground the NMI if you're not using it;
lda #<NMI_Lock
sta $fffa
lda #>NMI_Lock
sta $fffb // Set NMI Vector to point to RTI
ldx #$00
stx $dd0e // Stop CIA #2 Timer A
stx $dd04
stx $dd05 // Set CIA #2 Timer A to 0 cycles
inx
stx $dd0d // Enable CIA #2 Timer A as NMI source
lda #$81
sta $dd0e // Start Timer A and trigger NMI
bne *+3
NMI_Lock:
rti
@ CJ: "Given that you're still allowing the triggered IRQ to go ahead, the only effect of wrapping the above in SEI/CLI is that you're potentially delaying the response to the IRQ to after"
If you are fiddling with the IRQ vectors and not having the I-Flag set you're asking for it :) |
| |
ChristopherJam
Registered: Aug 2004 Posts: 1359 |
haha, writing to the NMI vector without disabling the pertinent CIA first is just as much asking for it.
SEI is no more effective at disabling otherCIA and VIC than turning off the relevant sources, and if you're *really* concerned about hostile/badly written code I've yet to see anything that would protect against someone using a never-returning once-per-frame NMI that immediately sets up a raster interrupt from scratch before falling into NOPs.
You're still going to be vulnerable right up to the point that you've written to both DC0D and I and there's not much you can do about that.
At some point you need to set some ground rules for the code you're shutting down. So yes, if you want to be tolerant of the preceding part setting enable bits in ISRs, by all means wrap your shutdown code in SEI/CLI. It still won't be ironclad, but it will indeed deal with a few more possibilities.
Alternately, either assume that enabling interrupt sources should only be done at startup, or make it the responsibility of the preceding part to shut down gracefully. |
| |
Copyfault
Registered: Dec 2001 Posts: 466 |
Quoting Martin PiperIf there was code running that used two or more interrupt sources to operate correctly, then using SEI/CLI would be advisable. This is because the code below without SEI/CLI is not atomic:
lda #$7f
sta $dc0d
sta $dd0d
ldx #0
stx $d01a
In other words, there are a few cycles between switching off IRQ source from CIA1, CIA2 and VIC2 where such complex code could misbehave, say for example by enabling the source in the IRQ that you've just switched off. This is what I was pointing at with my last post: an IRQ condition might become fulfilled during the execution of the STORE-opcodes that actually switch off further IRQ triggers by the source the STORE is affecting. The no. of cycles that it'll take to enter the irq routine depends on the cycle this irq condition was fulfilled - and the added comments in the example source code reflect my understanding of this delay.
Quoting Martin Piper]So using SEI/CLI is the correct bullet proof way of tackling that issue. I still don't understand why this should be needed. Switching off an IRQ source does just this; no further IRQs should be triggered after the source has been disabled, with the exception of the ones that got triggered while performing the disabling store opcode (see above).
But reading the other posts, I guess I have to ask: where am I wrong? |
| |
ChristopherJam
Registered: Aug 2004 Posts: 1359 |
Quoting Copyfault I still don't understand why this should be needed. Switching off an IRQ source does just this; no further IRQs should be triggered after the source has been disabled, with the exception of the ones that got triggered while performing the disabling store opcode (see above).
But reading the other posts, I guess I have to ask: where am I wrong?
You're not really wrong. The only edge case this misses is when an interrupt triggered by one source reenables interrupts from another source you've already shut down (eg a VIC interrupt setting up a CIA interrupt). |
| |
Copyfault
Registered: Dec 2001 Posts: 466 |
Quoting ChristopherJamargh, damn it. Give the possibility of "some other coder's VIC interrupt setting up a CIA interrupt or vise versa" I might actually need to reconsider my stance.
How hostile an environment do I want to guard against is the question I guess.. Ah ok, if the irq vecs have been changed already and need to be readjusted to some new routine, this might become tricky without SEI/CLI. Not completely impossible, but needs careful planning when the hi/lo-vecs are set.
In my former posts, I had a basic startup situation with its standard irqs in mind... |
| |
Copyfault
Registered: Dec 2001 Posts: 466 |
Quoting ChristopherJamQuoting Copyfault I still don't understand why this should be needed. Switching off an IRQ source does just this; no further IRQs should be triggered after the source has been disabled, with the exception of the ones that got triggered while performing the disabling store opcode (see above).
But reading the other posts, I guess I have to ask: where am I wrong?
You're not really wrong. The only edge case this misses is when an interrupt triggered by one source reenables interrupts from another source you've already shut down (eg a VIC interrupt setting up a CIA interrupt). Oh yes! So mixtures of IRQ sources have to be forbidden ;) But this'd narrow the possibilities (and thus the creativity) so I fear the revenge of evil SEI/CLI :( |
| |
chatGPZ
Registered: Dec 2001 Posts: 11088 |
The resistance to write proper code in favour of some 80s cargocult is impressing =D |
| |
Krill
Registered: Apr 2002 Posts: 2804 |
As has been demonstrated, it's not cargo cult under certain conditions. (It remains cargo cult when coming from BASIC.)
But then i'd consider setting interrupt masks from interrupt handlers bad practice. There are ways to effectively suspend interrupts without setting an interrupt mask, for both CIA timer and VIC raster interrupts, which would work in a ping-pong setup.
Now, any clean-up code after a demo part should know best how to safely disable all interrupts.
And it's still advisable to clean up things from within an interrupt handler. The I flag is set already, and the last interrupt has been acknowledged already or would have to be acked later anyways if not exiting yet. |
| |
chatGPZ
Registered: Dec 2001 Posts: 11088 |
Quote:it's not cargo cult under certain conditions.
sure, there are some RARE cases where its needed. but certainly not in the general case, or even in most cases. |
| |
Oswald
Registered: Apr 2002 Posts: 5007 |
good point with irq's reenabling irqs. I'll keep sei cli and ack cia irqs :) |
| |
ChristopherJam
Registered: Aug 2004 Posts: 1359 |
Quote: good point with irq's reenabling irqs. I'll keep sei cli and ack cia irqs :)
Don't forget to write to $dd0d before the others then SEI for a second time in case an NMI didn't restore the I bit. |
| |
TWW
Registered: Jul 2009 Posts: 541 |
Quoting ChristopherJamhaha, writing to the NMI vector without disabling the pertinent CIA first is just as much asking for it.
Yes, you should disable/ack CIA#2 interrupts first before grounding it / fiddling with the vectors. Here is the full code I use to setup a safe raster IRQ without presuming too much:
sei
// Disable/ack all CIA interrupts
ldx #$7f
stx $dd0d
lda $dd0d
stx $dc0d
lda $dc0d
// Bank in all available RAM except IO
lda #$35
sta $01
// Ground NMI
lda #<NMI_Lock
sta $fffa
lda #>NMI_Lock
sta $fffb // Set NMI Vector to point to RTI
ldx #$00
stx $dd0e // Stop CIA #2 Timer A
stx $dd04
stx $dd05 // Set CIA #2 Timer A to 0 cycles
inx
stx $dd0d // Enable CIA #2 Timer A as NMI source
lda #$81
sta $dd0e // Start Timer A and trigger NMI
bne *+3
NMI_Lock:
rti
// Set IRQ Vector
lda #<IRQ_Vector.getValue()
sta $fffe
lda #>IRQ_Vector.getValue()
sta $ffff
// Set Raster Interrupt Compare Value
lda #Raster_Compare_Value.getValue()
sta $d012
lda $d011
.if (Raster_Compare_Value.getValue()<256) {
and #%01111111
} else {
ora #$80
}
sta $d011
// Ack any pending Raster Compare IRQs (normally not neccessary)
lda #$01
sta $d019
// Enable Raster IRQs
// lda #$01
sta $d01a
// Allow IRQ's
cli
// async code
async:
jmp async
I consider this as safe as I can make it without making assumptions on what was.
Another important point is that if you are running async. code after the cli (instead of jmp *) you can get straight to that with no hassle and you are sure the VIC IRQ which was setup fires exactly when you told it and not by accident after the cli if some latent CIA IRQ fired (which it may have if you entered from basic). |
| |
chatGPZ
Registered: Dec 2001 Posts: 11088 |
you forgot the second SEI |
| |
Oswald
Registered: Apr 2002 Posts: 5007 |
|
| |
TWW
Registered: Jul 2009 Posts: 541 |
Quoting Groepazyou forgot the second SEI
If a NMI is executed between the SEI and the DD0D disable code, the Processor Status Register (PSR) gets pushed to the stack. When the inevitable RTI is executed the PSR is pulled from stack, the I-flag is restored without the need to set it a second time. |
| |
Trap
Registered: Jul 2010 Posts: 221 |
Wow. This sure sparked a discussion :) I read the thread a few times, but I honestly still don't feel I am any bit closer to knowing how I should deal with my problem?
Thank you all for dishing in - truly wonderful and much appreciated though :) |
| |
chatGPZ
Registered: Dec 2001 Posts: 11088 |
But what if the NMI handler manipulates the Stack? Are you assuming it doesnt? |
| |
Trap
Registered: Jul 2010 Posts: 221 |
I am not using NMI or reading any keyboard presses. Just a normal IRQ running with kernal off ($01=$35). |
| |
chatGPZ
Registered: Dec 2001 Posts: 11088 |
To answer your question we'd still need to know what exactly you are trying to do - there is no universal answer :) |
| |
ChristopherJam
Registered: Aug 2004 Posts: 1359 |
Quoting TrapI am not using NMI or reading any keyboard presses. Just a normal IRQ running with kernal off ($01=$35).
Then you can ignore almost all of the last 50 comments, and just
lda #$7f
sta $dc0d
ldx #0
stx $d01a
no SEI/CLI required. (that said, you do still need to ack the interrupt you're responding to if you do this in an ISR instead of in your main) |
| |
ChristopherJam
Registered: Aug 2004 Posts: 1359 |
Quoting TWWWhen the inevitable RTI is executed
We already covered this - the RTI is not at all inevitable if the interrupt in question is just setting up for another interrupt. |
| |
TWW
Registered: Jul 2009 Posts: 541 |
Quote: But what if the NMI handler manipulates the Stack? Are you assuming it doesnt?
If the NMI code messes with the stack, the RTI won't work, and chances are any previously running code prior to this would not have worked either.
@ Trap: If you setup and have control of your interrupts as I posted above, it should be straight forward to handle unpacking and transitions since you only need to handle Raster IRQs (assuming no funky memory banking).
// space is pressed inside a raster IRQ or whatever
// I-Flag is set since it's handled inside the IRQ
ldx #0
stx $d01a
inx
stx $d019
cli
// no more (internal) interrupts can happen here
jmp depack
PS. STACK is messed up at this point, so you cannot have any rti/rts. If the depacker sei/cli or not should not matter as long as it doesn't activate any IRQs. Finally ensure that there is no mem-bank trickery going on which may mess things up. |
| |
TWW
Registered: Jul 2009 Posts: 541 |
Quote: Quoting TWWWhen the inevitable RTI is executed
We already covered this - the RTI is not at all inevitable if the interrupt in question is just setting up for another interrupt.
If the interrupt in question is just setting up for another interrupt, you do not need to do anything except handle the ongoing *known* interrupt and it's source, assuming you *know* that no other internal interrupts may occur. |
| |
chatGPZ
Registered: Dec 2001 Posts: 11088 |
You are assuming quite a bit now - wasnt your code about not assuming anything and work always?
Quote:If the NMI code messes with the stack, the RTI won't work, and chances are any previously running code prior to this would not have worked either.
no?
nmiend:
bit flag
bmi notyet
plp
and #~4
php
notyet:
rti
(yes the working code looks a bit more elaborate, you get the idea) |
| |
TWW
Registered: Jul 2009 Posts: 541 |
Quoting Groepaz(yes the working code looks a bit more elaborate, you get the idea)
Yes I get the idea. Interesting argument technique, I use it myself sometimes...
So by your reasoning, would the 2nd sei in the code above mean that you consider it 100% safe?
If yes - Are you sure someone couldn't tailor some code to break it anyway if they really wanted to :)
If no - Why did you make the argument, knowing it's pointless?
Edit:
It just occurred that you could move the sei after disabling/ack'ing $dc0d. No need to have one before so you still only need one sei to counter the evil GPZ code above hehe :) |
| |
Trap
Registered: Jul 2010 Posts: 221 |
In case somebody has the same question, I'll post what I finally found to work. Thanks a million to all the great input from you guys.
Not 100% what I'm doing here, but it works :)
sei
lda #$36
sta $01
jsr vsync
lda #$0b
sta $d011
lda #$7f
sta $dc0d
sta $dd0d
lda $dc0d
lda $dd0d
lda #$48
sta $fffe
lda #$ff
sta $ffff
ldx #0
stx $d01a
inx
stx $d019
cli
jmp nextthinghappening
Thank you.
Trap |
| |
chatGPZ
Registered: Dec 2001 Posts: 11088 |
Quote:If yes - Are you sure someone couldn't tailor some code to break it anyway if they really wanted to :)
If no - Why did you make the argument, knowing it's pointless?
Oh, its not me who is saying his code is totally safe and makes no assumptions. Step back and look at what you posted in #62 - it will work pretty much exactly the same for all practical purposes, without the sei/cli :) Except when you try hard to break it, which will require exploiting similar uncommon scenarios as the one i posted.
trap: do the vsync (better: wait a frame) after setting d011 to $0b - then you can be sure it will really be blanked (the flag is only checked once per frame ~line f8 or sth) |
| |
TWW
Registered: Jul 2009 Posts: 541 |
Quoting Groepazits not me who is saying his code is totally safe and makes no assumptions. Step back and look at what you posted in #62
Here is what was actually said in post #62:
"Here is the full code I use to setup a safe raster IRQ without presuming too much"
If you're going to paraphrase, at least do it right.
Besides, it still only took to move one sei to counter your example of destructive code;
ldx #$7f
stx $dd0d
lda $dd0d
sei
stx $dc0d
lda $dc0d
Thanks for contributing to improve it. |
| |
chatGPZ
Registered: Dec 2001 Posts: 11088 |
you cant just move it, you must sei first to prevent that an irq triggers right after the stx $dd0d and then enables the NMI again.
Quote:
Here is what was actually said in post #62:
"Here is the full code I use to setup a safe raster IRQ without presuming too much"
Thats a really weak argument. How would anyone know exactly what you mean? Finding out how to do it "without assuming an undefined too much" (whatever it means) is not an interesting question at all. Making it 100% safe is. And if not - then what matters most of the time. And most of the time all those scenarios where one interrupt(source) triggers and enables another interrupt(source) as a result does not matter at all - making sei/cli or even ACKing the sources in the setup code a pointless exercise in most scenarios. Those things are only needed when trying to write code that considers those special edge cases. (And then... not considering all of them is ... a bit lame :=P) |
| |
Krill
Registered: Apr 2002 Posts: 2804 |
So... how about guarding a safe interrupt disable routine against a malicious VM running on 6502 itself, so any of the routine's instructions would be made ineffective if an attempt to disable interrupts (SEI, disable bits in an interrupt mask register, etc.) is detected? =) |
| |
ChristopherJam
Registered: Aug 2004 Posts: 1359 |
I'm still with groepaz on this one. For 99% of the parts out there, even if they don't shut themselves down properly all wrapping your cleanup code in SEI/CLI does is to add complexity. If I is clear, you can just turn off the sources and you're done - no acks required.
For the remaining 1%, TWW's solutions will work for some and not others - you really need to study the routine in question to see what you need, and honestly any routine with requirements so weird as to need anything more complex really should be responsible for cleaning up its own shit.
edit - and cf Krill's comment for an example of why there really is no such thing as a bulletproof routine :D |
| |
Krill
Registered: Apr 2002 Posts: 2804 |
Yeah, repeating myself: if you need to set interrupt masks from within an ISR, you're most likely doing it wrong and asking for trouble. =) |
| |
JackAsser
Registered: Jun 2002 Posts: 1987 |
Quote: Yeah, repeating myself: if you need to set interrupt masks from within an ISR, you're most likely doing it wrong and asking for trouble. =)
I enable/disable NMs from raster IRQs by messing with the mask. F.e. have a stable 4 line timer that I just mask/unmask in top / bottom border to enable the $d011 trickery needed. |
| |
Krill
Registered: Apr 2002 Posts: 2804 |
Quoting JackAsserI enable/disable NMs from raster IRQs by messing with the mask. F.e. have a stable 4 line timer that I just mask/unmask in top / bottom border to enable the $d011 trickery needed. Could the same effect be achieved by enabling/disabling the NMI handler's ack from a non-NMI context? (And acking previous NMI after re-enabling the handler's ack.)
Alternatively, have the 4-line timer not generate interrupts, but the other timer count just 1 underrun of the 4-line timer and generate interrupts. The latter can then be started and stopped from a non-NMI context without losing sync to VIC. |
| |
chatGPZ
Registered: Dec 2001 Posts: 11088 |
I also did something like this in Mongobong to play samples in borders using NMI and then have FLI in the display area. Dont remember details, it probably _was_ stupid to do it like i did :) |
| |
ChristopherJam
Registered: Aug 2004 Posts: 1359 |
Samples plus FLI? Now that is brave. |
| |
JackAsser
Registered: Jun 2002 Posts: 1987 |
Quote: Quoting JackAsserI enable/disable NMs from raster IRQs by messing with the mask. F.e. have a stable 4 line timer that I just mask/unmask in top / bottom border to enable the $d011 trickery needed. Could the same effect be achieved by enabling/disabling the NMI handler's ack from a non-NMI context? (And acking previous NMI after re-enabling the handler's ack.)
Alternatively, have the 4-line timer not generate interrupts, but the other timer count just 1 underrun of the 4-line timer and generate interrupts. The latter can then be started and stopped from a non-NMI context without losing sync to VIC.
Ofcourse u can, but less elegant and not really less error prone. A 4th line NMI is 100% predictable when it will trigger anyway. |
| |
TWW
Registered: Jul 2009 Posts: 541 |
Quoting Groepazyou cant just move it, you must sei first to prevent that an irq triggers right after the stx $dd0d and then enables the NMI again.
Enabling NMIs inside an IRQ... Never saw that myself, but not saying it doesn't exist.
Quoting Groepaz(And then... not considering all of them is ... a bit lame :=P)
Lame yourself :-D |
| |
Krill
Registered: Apr 2002 Posts: 2804 |
Quoting JackAsserOfcourse u can, but less elegant and not really less error prone. A 4th line NMI is 100% predictable when it will trigger anyway. Sure, but my point was to avoid setting interrupt masks in ISRs (without drawbacks, except maybe elegance, which is somewhat subjective).
Avoid the main cause for disabling without SEI/ACK/CLI wrapper failing. =) |