Some people seem to have problems with timing in their intros/demos/whatever, so my aim for this tutorial is to share my knowledge in this subject. I have divided this tutorial into two parts so that those who know how to use interrupts can skip the first part:
This is for everyone who doesn't know how to setup/use an intterupt. An interrupt is quite easy to setup though, this example is for a VBL-interrupt. VBL means Vertical Blanking, the interrupt will start every time the monitorbeam is at the top of the screen:
CurrentVBR holds the VBR (VectorBaseRegister) and VBL_int is the code you want to make every time the interrupt starts. a6 is ofcourse loaded with $dff000:
move.l CurrentVBR,a0 move.l #VBL_int,$6c(a0) move.w #$c020,intena(a6)
VBL_int: movem.l d0-d7/a0-a6,-(a7) ;save all registers ... bsr P61_Music ;.. or something else nice :) ... lea $dff000,a6 move.w #$0020,intreq(a6) ;clear IRQ flags move.w #$0020,intreq(a6) ;twice to avoid A4000 problems movem.l (a7)+,d0-d7/a0-a6 ;return all registers rte ;rte and not rts (e = exception)
A note: my experience shows that if you are using a Copper-interrupt, you'll have to clear the IRQ flags three times to avoid A4000 problems.
With frame timing I mean the way to make any effects run with the same speed on all computers. Not speed measured by frames per second (fps), but speed measured in the amount of frames you want to do per effect. Here is an example of what will happen if one doesn't do frame timing: Take a 3d scene engine and say you made a 3d scene that was 1000 frames long, timed it for your computer and put it n your demo. Let's also say another person who has a computer twice as slow as yours watched the demo. The result would be that it took twice the time to calculate one frame in the scene, and your scene engine would only render 500 frames instead of the intended 1000 frames. Oops, the person would only see half of the scene and wonder what the scene was all about, that is if you made a scene with some meaning :).
So we want the same amount of frames whether it is a vanilla (clean) Amiga1200 or an A4000/060 we are running on. The solution to this problem is framecounting. We use a framecounter that counts up to know which frame we should render. To do this we must also use something that runs at exactly the same speed on every single Amiga. Yes, interrupts!
I think the two usable interrupts for this are VBL and Copper (VERTB and COPER in the Hardware Reference Manual). It is as simple as increment our framecounter every interrupt time. Back to our example about the 3d scene. On the slower computer that runs at twice the speed, twice the amount of interrupts will be started for each scene frame thus making the framecounter to be running at the same speed on both computers.
Now to a implementation example, a routine of some sort where we want to render a number of frames:
Interrupt: ... move.l #FRAMECOUNT,d0 ;number of frames we have move.l framecounter,d1 ;the framecounter cmp.l d0,d1 bge .skip_add add.l #1,framecounter .skip_add: ...
The second implementation example is a blur routine, a tricky one that often is left out for the sake of optimizing. Without modifications, the routine blurs slower on slower computers thus making the effect too blurry. If that is a big problem frametiming is important: (Not a very good example)
move.w blursubctr,d1 move.w #0,blursubctr .yloop: .xloop: ... cmp.w d1,d0 ;d0 is the pixel to be set blt .putpixel sub.w d1,d0 .putpixel ... dbf .xloop dbf .yloop
And in the interrupt we just add one to the sub-counter:
Interrupt: ... add.w #1,blursubctr ...
There is however one problem. If your frame render is very slow, you might get overflow (d0 lower than zero). I told you it was a bad example :)
Last words: don't use interrupts without thinking first. Blitting a scroll in an interrupt is not a good idea for example. If you are uncertain if your timing is causing problems on slower/faster computers, you could always try to lower or raise the amount of pixels that you render. This is to "emulate" a slower or faster computer.