| |
Partz Account closed
Registered: Jun 2008 Posts: 17 |
Raster corruption
Just after a sanity check here really.... not begging for any deep analysis.
I've set up a simple raster interrupt of the form:
INT SEI ; Disable interrupts
LDA #$7F ; Disable timer interrupts
STA $DC0D
LDA #$01 ; Enable raster interrupts
STA $D01A
LDA #<IRQ ; Set up vector
STA $0314
LDA #>IRQ
STA $0315
LDA #$1B
STA $D011
LDA #$1 ; set up interrupt position
STA $D012
CLI
HALT JMP HALT
... So nothing to elaborate there. In my interrupt routine i have a font shown for about half the screen and then i'm changing font via $d018 further down the screen by busy waiting for $d012 to reach $85 an am also changing the screen colour at this point. What im finding is that 8 times out of ten my initial interrupt seems to trigger half way down the screen not at the top.... so for example, instead of he top half of the screen being white, only 1 raster line (approx) is white at or around position $85.
I haven't yet been able to find anything in my routines that would cause any corruption so was wondering if anyone else had experienced this and if maybe there was something wrong with my (admittedly niaive) set up above? As for code thats called in the interrupt its basically:
LDA #$1
STA $d019
LDA #$02
STA $d021
wait:
LDA $d012
CMP #$85
BNE wait
LDA #$0
STA $d021
JMP $EA31
... im doing a little more than the above - changing fonts, calling some music... but structurally is there any issue with the above in terms of waiting for the raster rather than setting up a second interrupt?
|
|
| |
JackAsser
Registered: Jun 2002 Posts: 2014 |
Structurally there is nothing wrong with the above. What bugs me is that you say you do a little more together with the fact that you acknowledge the interrupt immediatly. Consider for example that the little more brings you to a raster position > 1 after a wrap around and that you've acknowledged the IRQ. This will make the IRQ to retrigger again immediatly just after the RTI. F.e. somewhere around line $85 depending on how much extra the "little more" took.
Also I'm quite tired right now and can be plain wrong + that I really don't know shit about kernel stuff like 0314/0315/ea81/ea31, what registers are saved and what not... |
| |
Partz Account closed
Registered: Jun 2008 Posts: 17 |
Like i say, 8 out of ten times it works - but it is possible that something isn't being initialised correctly elsewhere and causing some routine to overrun due to some indeterminate state. I'll strip things down and see where this might be happening.
Thanks for the response. |
| |
TWW
Registered: Jul 2009 Posts: 545 |
Quote: Just after a sanity check here really.... not begging for any deep analysis.
I've set up a simple raster interrupt of the form:
INT SEI ; Disable interrupts
LDA #$7F ; Disable timer interrupts
STA $DC0D
LDA #$01 ; Enable raster interrupts
STA $D01A
LDA #<IRQ ; Set up vector
STA $0314
LDA #>IRQ
STA $0315
LDA #$1B
STA $D011
LDA #$1 ; set up interrupt position
STA $D012
CLI
HALT JMP HALT
... So nothing to elaborate there. In my interrupt routine i have a font shown for about half the screen and then i'm changing font via $d018 further down the screen by busy waiting for $d012 to reach $85 an am also changing the screen colour at this point. What im finding is that 8 times out of ten my initial interrupt seems to trigger half way down the screen not at the top.... so for example, instead of he top half of the screen being white, only 1 raster line (approx) is white at or around position $85.
I haven't yet been able to find anything in my routines that would cause any corruption so was wondering if anyone else had experienced this and if maybe there was something wrong with my (admittedly niaive) set up above? As for code thats called in the interrupt its basically:
LDA #$1
STA $d019
LDA #$02
STA $d021
wait:
LDA $d012
CMP #$85
BNE wait
LDA #$0
STA $d021
JMP $EA31
... im doing a little more than the above - changing fonts, calling some music... but structurally is there any issue with the above in terms of waiting for the raster rather than setting up a second interrupt?
Your initial set up looks correct.
However you can try this;
irq1:
lda #$01
sta $d019 //Ack the interrupt
lda #$85
sta $d012 //Set the next raster-interrupt
lda #$1b
sta $d011 //Don't forget this one
lda #$xx
sta $dd00
lda #$xx
sta $d018
lda #$xx
sta $d016
//Do you shit here
lda #<irq2
sta $0314
lda #>irq2
sta $0315
jmp $ea31
irq2:
lda #$01
sta $d019 //Ack the interrupt
lda #$01
sta $d012 //Set the next raster-interrupt
lda #$1b
sta $d011 //Don't forget this one
lda #$xx
sta $dd00
lda #$xx
sta $d018
lda #$xx
sta $d016
//Do you other shit here
lda #<irq1
sta $0314
lda #>irq1
sta $0315
jmp $ea31
Don't make the processor bussy to wait for something to happen. this is not what interrupts are about.
And if you still want to wait you'd be better off with:
lda #$85
cmp $d012
bne *-3
cheers! |
| |
SIDWAVE Account closed
Registered: Apr 2002 Posts: 2238 |
and nobody noticed that your code is bugged..
LDA #$1
STA $d019
LDA #$02 ;RED SCREEN
STA $d021
wait:
LDA $d012
CMP #$85
BNE wait
LDA #$0 ;BLACK SCREEN
STA $d021
JMP $EA31
Now look, at line $85 it sets d021 to 0, and immediately on next frame, it sets it to red.
simply there is a d012 wait missing before d021,2 |
| |
Kaizen
Registered: May 2009 Posts: 24 |
It seems that ACK CIA lacks...
SEI
LDA #$7F
STA $DC0D
LDA $DC0D <=== CIA1 interrupt acknowledge
etc.
;-) |
| |
MagerValp
Registered: Dec 2001 Posts: 1078 |
It's better to just use
lda #$7f
sta $dc0d
lda #<irq
sta $0314
lda #>irq
sta $0315
lda #$1b
sta $d011
lda #1
sta $d012
sta $d01a
without SEI - that way you don't end up unacknowledged IRQs. Just enable raster IRQs as the last thing you do.
|
| |
Stryyker
Registered: Dec 2001 Posts: 468 |
Without SEI there is a small chance an interrupt can happen while you're setting up your new one - say writing one of the IRQ vectors without the other one being written to.
I prefer to acknowledge the interrupts after I have set it up so I know when the next interrupt will happen. |
| |
MagerValp
Registered: Dec 2001 Posts: 1078 |
No IRQs happen after lda #<irq. If one occurs just as you disable it, it'll get acknowledged by the kernal IRQ handler before sta $0314, so it's perfectly safe. |
| |
Partz Account closed
Registered: Jun 2008 Posts: 17 |
Quote: and nobody noticed that your code is bugged..
LDA #$1
STA $d019
LDA #$02 ;RED SCREEN
STA $d021
wait:
LDA $d012
CMP #$85
BNE wait
LDA #$0 ;BLACK SCREEN
STA $d021
JMP $EA31
Now look, at line $85 it sets d021 to 0, and immediately on next frame, it sets it to red.
simply there is a d012 wait missing before d021,2
Am I missing a trick there? I thought it would set to red when the interrupt fired which had been set up to tricker in the vertical blank?
Actually Jan, I had a peek at some interrupt code in your TST Amiganoid demo to put me straight (!?!) does that one take you back??.... I was lusting after the 2x2 font ;-)
I'm now using multiple interrupts and all is working perfectly. Its something I had wanted to get more comfortable with in anycase. Its been nearly 25 years since I dropped c64 coding for the Amiga but to be honest - i never did get beyond the glitchy raster bar stage back then.
I hadn't intended leaving the busy wait in place... it was really just a place holder for an area I've set aside for some colour bars but now everything is a lot more robust now. Thanks for all the insights :-) |
| |
Martin Piper
Registered: Nov 2007 Posts: 722 |
Quote: No IRQs happen after lda #<irq. If one occurs just as you disable it, it'll get acknowledged by the kernal IRQ handler before sta $0314, so it's perfectly safe.
You cannot be 100% sure of that because the following code sequence is not atomic:
LDA #$7F ; Disable timer interrupts
STA $DC0D
LDA #$01 ; Enable raster interrupts
STA $D01A
Since it isn't atomic you cannot be 100% sure one IRQ will enable the other kind of IRQ after the first STA turns it off. |
| |
Graham Account closed
Registered: Dec 2002 Posts: 990 |
Has nothing to do with being atomic or not, there's simply something missing.
First disable the CIA A IRQs:
LDA #$7F
STA $DC0D ; disable CIA A IRQs
LDA $DC0D ; acknowledge CIA A IRQs <- important !
Now enable RASTER IRQ:
LDA #$01
STA $D01A ; enable RASTER IRQ
|
| |
MagerValp
Registered: Dec 2001 Posts: 1078 |
lda $dc0d is only needed if it's inside an SEI. If it's not, the kernal IRQ will simply trigger one last time, ack it, and then return to your setup code.
|
| |
Graham Account closed
Registered: Dec 2002 Posts: 990 |
And maybe mess up your stuff because you disabled Kernal or use lot's memory in the first mem pages :) I never CLI after depacking, hehe |
| |
Martin Piper
Registered: Nov 2007 Posts: 722 |
There is a reason why kernal always uses SEI/CLI pairs, it cannot assume what the user has been doing. The same is true of the user, you cannot always assume what the kernal or the previous user has been up to. It just takes someone using a turbo loader, or some other menu system that puts the IRQs into an unknown state before calling your application to cause a crash. Or even whilst moving code around in your own application you forget what IRQ setups have been done in the past. Which is why it is good defensive programming practice to always use SEI/CLI pairs. |
| |
MagerValp
Registered: Dec 2001 Posts: 1078 |
If you're executing after something other than the kernal, you of course need to account for that.
However, if all you're doing is disabling CIA#1 timer IRQs, and enabling raster IRQs, putting SEI/CLI around the code just creates a problem (from IRQs firing during setup), which you then have to add code to deal with.
A prime example is post #1 in this thread - raster IRQs are enabled (on an undefined raster line) 26 cycles before the handler is set up, needlessly creating a state that the IRQ handler might not handle gracefully (and only occuring once every 1000 launches or thereabouts, making it a bitch to debug). |
| |
TWW
Registered: Jul 2009 Posts: 545 |
Quote: If you're executing after something other than the kernal, you of course need to account for that.
However, if all you're doing is disabling CIA#1 timer IRQs, and enabling raster IRQs, putting SEI/CLI around the code just creates a problem (from IRQs firing during setup), which you then have to add code to deal with.
A prime example is post #1 in this thread - raster IRQs are enabled (on an undefined raster line) 26 cycles before the handler is set up, needlessly creating a state that the IRQ handler might not handle gracefully (and only occuring once every 1000 launches or thereabouts, making it a bitch to debug).
Im's sorry to ba a nubb but excactly how does IRQ's fire while code executing between SEI/CLI?
I figgured this for being the 'safe' way to set up a reaster-interrupt!? |
| |
Frantic
Registered: Mar 2003 Posts: 1648 |
@TWW: Not "fire" in the sense that irq handler code will be called, but an irq-flag may be set when in SEI state and precisely because no irq handler code will be executed when in SEI state the irq will not be ack'ed. ...unless one also adds code to acknowledge any irq's that may have fired after the SEI before the previously enabled sources of IRQs are disabled.
Not that it matters a whole lot. In either case, it is not a matter of a tremendous amount of code. :) |
| |
Stone
Registered: Oct 2006 Posts: 172 |
I'm a bit rusty, so I hesitate to join this discussion, but I have to agree with Martin Piper: Unless you are a 100% sure of what you're doing (and the OP clearly isn't), set up your interrupt inside a SEI/CLI block and acknowledge both CIA#1 and raster interrupts before CLI'ing. |
| |
MagerValp
Registered: Dec 2001 Posts: 1078 |
And there precisely is my point: setting it up inside an SEI/CLI block makes the code more complex and much easier to get wrong, especially if you're a beginner.
A good example of when you should use SEI/CLI is if you do non-atomic changes to the IRQ while it's running, such as redirecting the IRQ vector, or changing the IRQ raster line with both d011 and d012.
However, during setup, the procedure is:
1. disabled previous IRQ source
2. acknowledge any old IRQs
3. set up new IRQ source
4. set up new IRQ vectors
5. enable new IRQ source
None of these actions need to be done inside an SEI, and doing so creates an unnecessary step 6 where you have to acknowledge IRQs triggered inside. The only "trick" with my code is that step 2 is handled by the previous IRQ handler, instead of explicitly by the setup code.
|
| |
Frantic
Registered: Mar 2003 Posts: 1648 |
Are there any known environments in which a C64 program may be executed without the default CIA1_TimerA interrupt being the only IRQ enabled? I guess not? |
| |
Martin Piper
Registered: Nov 2007 Posts: 722 |
Quoting MagerValp1. disabled previous IRQ source
2. acknowledge any old IRQs
3. set up new IRQ source
4. set up new IRQ vectors
5. enable new IRQ source
None of these actions need to be done inside an SEI
Incorrect. If an NMI enables any IRQ source and causes an IRQ to be signalled between 3-4 then the IRQ can be triggered with one half of the IRQ vector set and the other half set to the previous value. This is precisely why SEI/CLI is needed to stop the IRQ being triggered until both bytes for the vector are set correctly.
Or if a VIC generated IRQ enables a timer, or vice versa, you have a similar situation where your setup without SEI/CLI causes the IRQs to be setup with bugs.
This is because on the C64 there are two sources for IRQs, the VIC and the CIA. It is not possible to clear the CIA and VIC generated IRQs in an atomic operation so while you switch off one you may have the other course generate an IRQ, which can then enable the other source and trigger another IRQ. Therefore you really do need SEI/CLI pairs.
|
| |
MagerValp
Registered: Dec 2001 Posts: 1078 |
Absolutely, if you have chained setups with IRQs and NMIs spawning other IRQs, you're WAY beyond the basic setup we're talking about here. I'm not saying SEI is useless, I'm saying people use it wrong a lot of the time. |
| |
Martin Piper
Registered: Nov 2007 Posts: 722 |
Right, so going back to the first post, there is an indeterminate IRQ state. It might be chaing the code from the kernel, or a previous part, of after a long decompress with the IRQs disabled. We are not sure.
So the thing to do is to really make sure with a belt and braces approach. i.e. The IRQs are disabled, with SEI, then turn off both IRQ sources (VIC and CIA), then setup the raster as a source remembering to also store the raster high bit as required, then set vector, then ACK all IRQ sources (CIA and VIC) then lastly CLI. The idea being that the IRQ is only triggered by a real IRQ signal that we requested and not by something that might have happened thousands of cycles ago.
In other words if SEI/CLI were not used for the original post that won't fix the issue and could lead to problems with certain setups or if the code is relinked after another part etc. It is generally better to be safe rather than assume the state is known. This is because a part only needs to be relinked or ordered differently and then without a comprehensive IRQ setup you might get wobbly rasters or a crash with code that was previously making an assumption but working in that one particular place. |
| |
chatGPZ
Registered: Dec 2001 Posts: 11386 |
Quote:It is generally better to be safe rather than assume the state is known. This is because a part only needs to be relinked or ordered differently and then without a comprehensive IRQ setup you might get wobbly rasters or a crash with code that was previously making an assumption but working in that one particular place.
while i agree with magervalp for the most part, i agree with this even more :) spending a few more bytes and a couple of braincells on making the code *really* safe can never be wrong, and *will* save you some headache at some point :) |
| |
Stone
Registered: Oct 2006 Posts: 172 |
I guess people are never going to completely agree on this.. Another tip though for avoiding nasty corner cases and first-frame glitches and potential crashes, is to wait for a specific raster position before enabling raster interrupts. |
| |
SIDWAVE Account closed
Registered: Apr 2002 Posts: 2238 |
rip some code from a 1987 demo by TST, it always works :D |
| |
MagerValp
Registered: Dec 2001 Posts: 1078 |
Quoting Groepazwhile i agree with magervalp for the most part, i agree with this even more :) spending a few more bytes and a couple of braincells on making the code *really* safe can never be wrong, and *will* save you some headache at some point :)
Braincell spending is what I'm advocating here. People have been cut'n'pasting IRQ setup code with timing bugs for over 25 years. |
| |
Partz Account closed
Registered: Jun 2008 Posts: 17 |
Wow.... I never imagined I would provoke such an evocative thread. Just to recap - I'm now using multiple IRQ's at specific vertical raster positions and everything is fine. Going back to the original issue I ran an older version of my code with the LDA $DC0D in place and ran up the app multiple times without the initial problem rearing its head.
I hadn't appreciated that the code as was could have been so fragile to initial state but clearly it can be (and was). There was a time 25 years ago when it all made sense, sad to think you can cram your head with so much stuff and then lose it (would never have thought I'd need the PRG to turn on multi colour mode!). I guess there is more I could do but i've now got something that consistently gives me what I want. Now if I could find some nice 2x2 fonts i'll be sorted :-)
Jan, I did look at one of your TST demos - more to see how your colour bar timings than your start up though ;-).
Thanks again for all the detailed replies. |
| |
Martin Piper
Registered: Nov 2007 Posts: 722 |
We are programmers, everything code related is evocative. ;) |