Jump to content
IGNORED

DLI and Timer 2 interrupts...


Recommended Posts

I have a mouse routine that runs off of timer 2. When I combine it in a program with DLI's running to change background color in mid-screen, the mouse interrupt appears to mess up the vblank timing, most notably in the reload of default colors..the DLI-color wraps back around and is still set at the top occasionally ( you can see the top part of the screen rapidly flickering between the dli-color and the default ). I figure its got to be these two routines interacting..and I am guessing that occasionally the timer 2 intterupt starts firing during the vertical blank, and either uses too many cycles or messes something else up. So, I am theorizing that if I blocked the timer2 interrupt from running during vlabk, that might help, but I am not sure how to do that, other than a VBI, but I think I'd still be in race then. Does this make sense or am I on the wrong track?

Link to comment
Share on other sites

Hmm, well that was interesting. I found that if the very first statement in the body of the audf2 interrupt routine was a store to STIMER ( $D209 ), then the screen distortion went away. If it was one of the last instructions, it ddidnt do anything. I am glad I found it by guessing but I don't understand whats actually going on. The STIMER store reloads the counters.

Link to comment
Share on other sites

VBI will skip the Stage 2 (including any user routines vectored through $224) if the "I" flag is set - in other words if your Timer routine happens to be running when the VBI occurs, most of the shadow register copies to hardware registers (including most of the colour regs) won't happen.

 

So long as your VBlank user routine doesn't run too long, it's probably best to run it as a Stage 1 (through $222).

 

There are also instances where an IRQ can sufficiently delay a DLI such that a colour change is delayed - and if an IRQ or user program uses WSYNC it can sometimes delay a DLI by an entire scanline.

Link to comment
Share on other sites

With timers, I found that anything time-critical will be too slow since the system IRQ routine has them way down the list of sources it tests.

 

Might be best to write your own Immediate IRQ handler - test for Timer2 first, then just jump to the normal system IRQ handler if it isn't the IRQ source.

Link to comment
Share on other sites

You mentioned VBI being interrupted. Should have mentioned before, Stage 2 VBlank always runs with IRQs enabled - shouldn't be a problem, however it means that it can be delayed somewhat.

 

Since Timer routines also take some time to clear the Interrupt, re-enable, then Start Timer again, the best bet is to try and make the routine as efficient as possible, ie - use Z-page, don't perform any unnecessary calculations (do them elsewhere, like a VBI).

Link to comment
Share on other sites

Hang on. You're talking about Timer2 the "software" timer?

 

I was thinking POKEY Timer 2.

 

OS Timer 2 is handled in Stage 2 VBlank, so will be periodically skipped.

 

I/O to tape/disk, and even a keypress is sufficient to cause Stage 2 to be skipped.

 

If you're doing something that just can't be skipped each frame, then an Immediate VBlank routine is best. Timer 1 is used by the OS, so best not to use it.

Link to comment
Share on other sites

No, you are correct, I mean the pokey timer. It is easy to get confused with the system ones.

 

void hook_timer2( unsigned char audctl,

unsigned char audc2,

unsigned char audf2,

void (*vector)(void) )

{

timer_2_vector=vector;

_audctl=audctl;

_audc2 =audc2;

_audf2 =audf2;

 

asm("lda $212"); //VTIMR2

asm("sta _old_t2");

asm("lda $212+1");

asm("sta _old_t2+1");

asm("lda #<_timer_2_wrapper");

asm("sta $212");

asm("lda #>_timer_2_wrapper");

asm("sta $212+1");

asm("lda __audctl");

asm("sta $D208"); //AUDCTL

asm("lda __audc2");

asm("sta $D203"); //AUDC2

asm("lda __audf2");

asm("sta $D202"); //AUDF2

asm("sei");

asm("lda $10"); //POKMSK

asm("ora #2"); //timer 2 enable

asm("sta $10"); //POKMSK

asm("sta $D20E"); //IRQEN

asm("sta $D209"); //STIMER

asm("cli");

}

Edited by danwinslow
Link to comment
Share on other sites

OK. I lost track there a bit.

 

How frequently is your Timer routine being called? If it's greater than once every 2 scanlines or so, then you're probably best off stealing the Immediate IRQ vector.

 

And to reitterate - Stage 2 VBlank is viewed by the OS as a non-critical process. It is routinely skipped - a keypress has about a 1 in 15 chance of causing it.

 

A POKEY timer routine can also cause Stage2 to be skipped, especially if it's a very frequently occuring one.

 

 

You mention you're using DLIs anyway. Why not just ditch the Timer routine and do your mouse checking as a VBI, and also have the DLI do it as well. 100 samples/second should provide reasonable precision.

Link to comment
Share on other sites

You are implying that the pokey interrupt sets CRITIC on its own, right? Thats what would cause the stage 2 skip. By the time I get control ( after the immediate IRQ handler ) then CRITIC has already been set, which is messing things up.

 

Just checkin to make sure I understand. I appreciate the help.

Link to comment
Share on other sites

CRITIC is only one condition to skip Stage 2. IRQs being in a disabled state is another condition which will cause Stage 2 to be skipped.

 

The OS code checks the Processor Status which gets pushed onto the stack when the VBI is triggered.

 

If the I bit is set, that means one of two things:

 

- A SEI instruction caused it.

- an IRQ occurred just before VBlank. To prevent nested IRQs, the 6502 sets the I flag whenever an IRQ occurs. The RTI instruction restores the I flag to it's previous state.

 

So, to prevent glitches, you have to use a Stage 1 VBI routine. If you have rotating colours, then you should have a Stage 1 routine that stores the colours to the hardware regs.

Link to comment
Share on other sites

Well, although nearly everything we discussed was in play, the actual problem was that this is a really bad thing to have in the middle of your interrupt code :

 

screen_ptr+=(mousey * 40 );

 

That generated too much overhead with all the C promotion and multiply and other tomfoolery. Put that into efficient assembler and no more problem.

Link to comment
Share on other sites

Well, although nearly everything we discussed was in play, the actual problem was that this is a really bad thing to have in the middle of your interrupt code :

 

screen_ptr+=(mousey * 40 );

 

That generated too much overhead with all the C promotion and multiply and other tomfoolery. Put that into efficient assembler and no more problem.

 

You're writing interrupts in C? Are you saving all the zeropage C locations and then restoring them afterwards, or does it just work without having to do that? (never tried it myself)

Link to comment
Share on other sites

Hi shawn. Yes, in C with the cc65 compiler. What I do is to use a function like

 

void foo( void )

{

asm("rti");

}

 

With a parameterless void function, cc65 winds up just generating a basic lable, with no data stack or zero page shenanigans. Then, I mostly just use the asm("") function to embed assembler. It is possible to use C code, but you have to look at the code for the functions you are calling to see what you have to save from ZP. It's more or less not worth it to use straight C...anything you do thats complicated enough to warrant it tends to be too slow. Probably the only interrupt thats would be suitable would be a deferred VBlank.

But, if you are careful, you can write simple stuff in C ( not using any of the library functions, that is ) and cc65 generates some pretty good code. So, what happened to me is that I didn't know offhand how to do a multiply by 40 in asm, so I weenied out and stuck a C multiply in there.

Link to comment
Share on other sites

*40 is pretty straightforward since 40 = 8 + 32

 

So, you just rotate left (with Carry initially clear) 3 times, store it in Var1.

 

Rotate another 2 times (carry intact), store it in Var2.

 

Then add Var1 and Var2.

 

Although, it is time consuming... table lookup for constant multiplies is always quickest.

 

Or, take it a step further and just have tables with the actual low/high screen address bytes.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...