| |
oziphantom
Registered: Oct 2014 Posts: 490 |
Force a queded IRQ
Is there a way to pogrammatically ( without the CIAs ) to en-queue an IRQ from within an NMI?
BRK will interrupt an NMI.But is there some other trick, maybe some stack fiddling? |
|
| |
TWW
Registered: Jul 2009 Posts: 545 |
I perhaps do not understand what you are trying to achieve but the Interrupt flag let's you control IRQs so whenever the flag is set, no IRQ's will trigger and when clear, they will.
This should allow you to control when they are triggering from a NMI or in general.
If you need timed (delayed with a certain amount of cycles) you could use the CIA and allow the IRQ to trigger just to set a flag and return which you in then could monitor in the NMI.
Can you elaborate a bit on what you are trying to do? |
| |
oziphantom
Registered: Oct 2014 Posts: 490 |
A software interrupt.
However it can if needed rely on the fact it is in a NMI.
I doubt its possible, but you never know there might be some fancy trick to get it ;) |
| |
TWW
Registered: Jul 2009 Posts: 545 |
You can trigger a SW interrupt whenever you want by using BRK or SEI/CLI in combination with a timer or raster IRQ which always is set to trigger.
I think I still don't get what you are trying to do ;-) |
| |
oziphantom
Registered: Oct 2014 Posts: 490 |
BRK won't force an interrupt, as it will invoke the break handler right then and there, even if you are in an NMI.
So the ideal path is
Enter NMI
Do stuff
Optionally Queue IRQ
Do other stuff
Exit NMI
IRQ triggers when it can, if queued. |
| |
Oswald
Registered: Apr 2002 Posts: 5094 |
how about setting up a raster irq (outside of available raster range), and using d019 to artifically fire a raster irq ? not sure if d019 can be used in this way tho.
similar thing could be achieved via timer interrupts too. |
| |
Flavioweb
Registered: Nov 2011 Posts: 463 |
If you do a CLI as first instruction of your irq code, when another irq happens it will be triggered as well...
If it's what you mean... |
| |
Mixer
Registered: Apr 2008 Posts: 452 |
If you wish to exit NMI directly to irq handler, you have to push the irq handler address to stack so that RTI fetches it correctly.
The irq handler then runs, but there is no flagged interrupt going on, so this execution can be interrupted, unless masked.
If you want some certain interrupt to take place, then set the conditions for that interrupt(VIC/CIA) etc. |
| |
chatGPZ
Registered: Dec 2001 Posts: 11386 |
Quote:BRK won't force an interrupt
in fact that is _exactly_ what a BRK does. |
| |
White Flame
Registered: Sep 2002 Posts: 136 |
It's faster to just tail-call right into the IRQ, than it is to exit the NMI and then fake entering the IRQ handler via stack munging.
Neither the stack nor the interrupted program see any difference whether or not it was interrupted via NMI or IRQ, or if it were interrupted twice (NMI falling into IRQ after RTI) instead of once. If the prologues of both interrupt handlers match (which normally would be pushing regs to stack, instead of STA'ing them), you can do a "real" tail call:
nmi:
prologue
process
test
JMP irq_noprologue
epilogue
irq:
prologue
irq_noprologue:
process
epilogue
If the prologue/epilogue don't match, then you should do a simple test & compare after restoring saved registers. Since the processor status flags are restored as part of RTI, you don't have to worry about them, so a BCS or a BIT+BNE can perform the test right before the RTI instruction of the NMI handler, depending on how you saved the result of the decision to branch to IRQ. |
| |
oziphantom
Registered: Oct 2014 Posts: 490 |
Quoting GroepazQuote:BRK won't force an interrupt
in fact that is _exactly_ what a BRK does.
Fair I was loose with my terminology.
BRK won't force an IRQ.
It will force a jump to the FFFE/FFFF vector, but not under the rules of an IRQ. |
| |
Flavioweb
Registered: Nov 2011 Posts: 463 |
Quote: BRK won't force an interrupt, as it will invoke the break handler right then and there, even if you are in an NMI.
So the ideal path is
Enter NMI
Do stuff
Optionally Queue IRQ
Do other stuff
Exit NMI
IRQ triggers when it can, if queued.
Isn't this the normal behaviour of a maskable irq triggered during a NMI? |
| |
Bitbreaker
Registered: Oct 2002 Posts: 508 |
Quoting oziphantom
So the ideal path is
Enter NMI
Do stuff
Optionally Queue IRQ
Do other stuff
Exit NMI
IRQ triggers when it can, if queued.
But then it is sufficient to just push the address (+ bogus flags) on the stack to queeue in another task. Then the rti at the end of the NMI handler pulls that stuff form stack again and sets the PC to said address. Then, when th queued irq is also finished with rti, the stuff before entering the nmi is continued. Take care of the registers though and in case also push/pull them from stack.
Other ways are to never acknowledge some irq (lda $dd0d) to make it happen just again and again when you need it, or stop it from happening again by acknowledging. |
| |
TWW
Registered: Jul 2009 Posts: 545 |
Quoting oziphantom
So the ideal path is
Enter NMI
Do stuff
Optionally Queue IRQ
Do other stuff
Exit NMI
IRQ triggers when it can, if queued.
Isn't this normal NMI/IRQ behaviour? Let's say you are playing samples, the sample-player will trigger at the set frequency (by a CIA #2 timer) while any triggered IRQ (typically raster) is executed once the NMI is exited (RTI - clearing the IRQ flag). |
| |
oziphantom
Registered: Oct 2014 Posts: 490 |
Yes that is Standard IRQ behaviour, which is what I'm looking for. I'm pointing out the standard IRQ behaviour to show why you can't just use BBK.
The custom behaviour I'm asking about is "Is there a way to achieve an IRQ in software, such that it behaves in the standard way."
I could push fake status, then address where address is the IRQ routine + bytes to skip its entry, the issues with it is I need to store 2 address ( 1 normal entry and then 1 NMI entry) and possibly set them in multiple places, which could be mitigated by storing my own NMI entry vector and the code look its up Or all IRQs have a fixed length entry and thus the NMI takes IRQ address + constant then store. I could also do read d012 add 2 with care to make sure its not out of range, then set D012 again and enable to interrupt flag, if I had a free CIA IRQ timer I could use it.
However all the balancing acts, double vectors management etc goes away if you can just trick the 6502 into thinking it has a pending IRQ it needs to do. Hence I'm asking to see if there is any way to do such a thing. |
| |
soci
Registered: Sep 2003 Posts: 480 |
I think this is what WF meant:
nmi ;...
bcs do_queued_irq
rti
do_queued_irq jmp ($fffe)
Looks simple to me. But if there's a real IRQ waiting it's not really queued any more. |
| |
TWW
Registered: Jul 2009 Posts: 545 |
The only way to initiate a SW controlled IRQ is by doing a BRK instruction, clearing the I-flag when you know (or have deliberately forced) an IRQ condition or mimic the interrupt handler behaviour (push PC, SR and do a JMP (IRQCODE) which I believe is what WF and Soci is pointing at)
The IRQ line is checked between instructions and as you know trigger the IRQ handler if it is high. The BRK does not pull the line high but triggers the handler anyway (and set's the BRK flag so you have control if the IRQ was launched by a voltage (Cartridge port IRQ line, CIA or VIC) or by SW).
At least this is how I understand it. |
| |
Hein
Registered: Apr 2004 Posts: 954 |
Maybe you want to interrupt an NMI? |
| |
Oswald
Registered: Apr 2002 Posts: 5094 |
I think the issue at hand could be solved with a more sane aproach. |
| |
chatGPZ
Registered: Dec 2001 Posts: 11386 |
Quote:BRK won't force an IRQ
in fact a IRQ will put a BRK into the instruction decoder.... |
| |
soci
Registered: Sep 2003 Posts: 480 |
Quote: Quote:BRK won't force an IRQ
in fact a IRQ will put a BRK into the instruction decoder....
On the schematics it looks like it pulls the data lines low in front of the instruction register to make the next instruction a BRK. Not that it's important ;) |
| |
chatGPZ
Registered: Dec 2001 Posts: 11386 |
lol. yes. (there was some other subtle difference ... i forgot =P) |
| |
Oswald
Registered: Apr 2002 Posts: 5094 |
soci, thats a nice detial, now we know why its 00 :) |
| |
lft
Registered: Jul 2007 Posts: 369 |
Quoting OswaldI think the issue at hand could be solved with a more sane aproach.
This.
Oziphantom, what is the ultimate goal hidden behind your question? |
| |
Karmic
Registered: Apr 2015 Posts: 66 |
Yep, I've been trying to wrap my head around this question.
WHY do you need it to behave exactly like a normal IRQ? WHY can't you just jump to your IRQ handler, or use BRK? |
| |
White Flame
Registered: Sep 2002 Posts: 136 |
TWW: The IRQ line is actually active low, so it trips an interrupt when it's grounded, not high. Most C64 signals work like this, with a pull-up resistor defaulting lines to 5V, with any number of chips optionally asserting ground, so no conflict between multiple chips asserting a signal will let out the magic smoke.
Quoting oziphantomYes that is Standard IRQ behaviour, which is what I'm looking for. I'm pointing out the standard IRQ behaviour to show why you can't just use BBK.
The custom behaviour I'm asking about is "Is there a way to achieve an IRQ in software, such that it behaves in the standard way."
You really need to define what "standard behavior" is and why you want it. The only "proper" standard behavior would be to tell another chip to hold /IRQ low. Again, the interrupted code won't perceive anything no matter which option you take, besides the passage of time. You need to list what effects you want to happen. Are you using the stack droppings left over from the interrupt? Do you want it as fast as possible? Those are about the only 2 scenarios I can think of where any of this matters. And a simple software jump will be the fastest route, leaving the NMI's return information on the stack to use to exit the IRQ. |
| |
ChristopherJam
Registered: Aug 2004 Posts: 1409 |
Quoting lft
Oziphantom, what is the ultimate goal hidden behind your question?
Yes, this is sounding a lot like an XY problem to me. |
| |
oziphantom
Registered: Oct 2014 Posts: 490 |
Quoting lftQuoting OswaldI think the issue at hand could be solved with a more sane aproach.
This.
Oziphantom, what is the ultimate goal hidden behind your question?
Its a way of cheating another thread.
So you have the case where some logic is do be done in the "Main loop", you have some other logic that eats CPU - decompression for example, and you have NMIs
So while you have the main loop, you also want to decompress stuff, this means you need to make your decompression routines ( of which I have a few) be state aware, in that each of their internal loops needs to check ( has nmi told me I need to do a thing ) and then break out, do the thing, come back and carry on decompressing.
Once they have finished decompressing, the main loop resorts back to "wait for nmi to tell me do a thing".
The way to simplify and clean up all of this logic is to re-purpose the IRQ to "do the thing". So when you are decompressing you tell the NMI to handle "do the thing", when you are not decompressing you don't tell the NMI to do it and let the main loop take care of it. When I'm in the case that neither the main loop nor the NMIs are doing the thing duty, then the decompression system can trigger the thing.
to which the logic now looks like thanks to Soci
[nmi handler]
LDA NMIDoThing
LSR
PLA
TAY
PLA
TAX
PLA
BCS _queueIRQ
RTI
_queueIRQ
JMP ($FFFE)
inside the decompression routine
LDA DecompressDoThing
BEQ +
BRK
+
Main Loop
- LDA DoThing
BNE -
BRK
JMP -
Thus the NMI is unchanged, it still happens exactly as it was before with its tight timings, the "fragment" or "thread code" is now written in one way, it saves and restore its registers and ends in an RTI, and its updated from a single Vector location, without any management of special cases, allowing the NMI, decompression, VIC or mainloop logic to be generic and used in all cases, with simple control of who is responsible for doing what. Giving me an effective thread that I can have strict timing control, or loose timing control over as needed, and can be passed around very easily allowing me to make any combination needed for the user experience desired. |
| |
Oswald
Registered: Apr 2002 Posts: 5094 |
"So while you have the main loop, you also want to decompress stuff"
this is usually solved in demos with re-entrant irqs. The main effect is jumped to from an irq, which immediately sets a flag saying not to jump here again in next frame from the irq, and then also a CLI, so upcoming irqs will be served. (all irqs must use stack properly and save registers on stack).
the 'main' cpu thread is used by the loader in the remaining frame fragment when all irqs have been returned. that is the main effect finished running, and there's still some time left until the raster irq starts, that initiates it.
a more primitive way I was doing simply adding a byte counter to the loader, and having it rts after every xth byte. or setting a flag if finished.
I also did multitasking, 2 raster irqs exchanged contents of stack. but better virtually halving the stack and only change stack pointer when changing context. |
| |
oziphantom
Registered: Oct 2014 Posts: 490 |
The BRK/NMI/IRQ are three faces of the same coin
type interupt NMI interupt IRQ Vector needs to exit with RTI
JMP No No whichever you call No
IRQ No No FFFE/F Yes
NMI Yes Yes FFFA/B Yes
BRK Yes Yes FFFE/F Yes So the Standard behaviour I want is the IRQ case. In that is the standard behaviour of the IRQs execution not the Standard behaviour of the IRQ inception.
So outside of an NMI, BRK is a fine substitute for doing an IRQ. Inside an NMI it doesn't substitute for an IRQ.
I'm looking for a way to get the IRQ operation, which is basically to "en-queue" the IRQ on the CPUS "things to do" in a neater way . i.e it gets to the of the NMI and goes "oh yeah I have an IRQ to do as well" *goes and does IRQ*
But I'm not looking for the solution, I'm asking if there is a neater solution.
To which Soci found at this point what I'm going to call the neatest. In that the can use the C flag to "chain" the IRQ onto the end of the NMI. As a PLA will modify N and Z but not C. Any modification of C is restored by the RTI which will do the PLP for you. Thus the C is safe to modify before the stack pulls, and react to it after them. This allows you to transparently and safely jump straight into the IRQ handler, as the stack has now been restored to its "first clock of interrupt state". So no extra handling is needed to handle this "queued interrupt".
And I'm sure the crackers will appreciate such madness being well documented here for them ;) |
| |
Flavioweb
Registered: Nov 2011 Posts: 463 |
* = $080D
LDA #$7F
STA $DC0D
LDA $DC0D
LDA #$35
STA $01
LDA #<NMI_IRQ
STA $FFFA
LDA #>NMI_IRQ
STA $FFFB
;-------------------
MAIN_LOOP
LDA QUEUED_FLAG
EOR #$01
STA QUEUED_FLAG
JMP MAIN_LOOP
;-------------------
NMI_IRQ
STA RESTORE_A
QUEUED_FLAG = *+$0001
LDA #$01
BEQ NMI_NO_QUEUE
LDA #>QUEUED_IRQ
PHA
LDA #<QUEUED_IRQ
PHA
PHP
NMI_NO_QUEUE
INC $D020
LDA $DD0D
RESTORE_A = *+$0001
LDA #$00
RTI
;-------------------
QUEUED_IRQ
SEI
INC $D021
RTI
;---------------------------------------
The "QUEUED_IRQ" code act as a "normal" IRQ and is queued to a NMI but can be interrupted by a NMI but not by another maskable IRQ...
The QUEUED code will be executed accordingly to "QUEUED_FLAG" value.
Should be something like this?
(Edit: press RESTORE key to fire the NMI...) |
| |
oziphantom
Registered: Oct 2014 Posts: 490 |
If you don't need to push the A,X,Y etc in your handler than you can do it that way. |