> >I also have several docs from the internet (Word docs) about the timer and >its internals and programming it - these are about 60kB so ask me >neil@tuva.demon.co.uk and I can email them to you if you wish. > >I hope this information is followable and of some use. I've added in some >of my code fragments to show what I am attempting to do with my code - I'll >happily discuss these methods further (off-list?) > >Cheers and Good Luck! > >Neil > > ---------- > >************************* xbtimer() function ************************ > >(31) xbtimer >VOID xbtimer(timer, control, data, vec) >WORD timer, control, data; >LONG vec; >'timer' is the timer number (0, 1, 2, 3 corresponding >to 68901 timers A, B, C and D).'control' is the timer's >control-register set- ting. 'data' is a byte shoved into the >timer's data register. 'vec' is a pointer to an inter- rupt >handler. Timers are allocated: >Timer Usage >A Reserved for end-users and applications >B Reserved for graphics (hblank sync, etc.) >C System timer (200hz) >D RS-232 baud-rate control (this timer's interrupt vector is >available to anyone) > >********************************************************************** >From: "Thomas Redelberger" >Subject: Re: Interrupt and timer >Date: 27 February 2001 17:55 > >Hi Neil! > >You *can* do more often than the 200HZ system timer. The code below is from an application I wrote some time ago to peek regularly the program counter to look for performance bottlenecks in programs: > >========= install stuff (assembler syntax!) ============= > >* install timer A delay mode >* prescaleling 4=1/50, data 5 = 100 microseconds/tick > xbtimer #0,#4,#5,pcspy > >... what ever you like to do... > > xbtimer #0,#0,#0,0.w ; switch off timer > >============================================== > >============= the interrupt routine itself =============== > >pcspy movem.l pcs_rgs,-(a7) ;save all used registers > >.... your stuff ... > > movem.l (a7)+,pcs_rgs ; restore registers > > bclr.b #5,$fffffa0f.w ; clear "int. in service" bit > rte > >=============================================== > >As you see installing could be done with C but the interrupt routine >requires assembler. >I think timer A was unused in all TOS versions. I heard that MAGIC uses >timer A for some purpose but I am not sure. > >Regards >Thomas > > >******************************************************************* > > > Is it possible to set the address for a timer interrupt routine > > to go to a C function? > >Yes. If you call xbtimer in your C-code you can supply a C function as an argument. But note that the requirements on this function are: > >- must save all used registers >- must end with rte *not* with rts > >and > >- stack checking that is done by some compilers can not be used. > >If I were to do what you are doing (I use Turbo-C 2.0), I would still need to write a small assembler function that fulfills the requirements and call a C function from inside. The assembler function would "wrap" the C-function. >However an interrupt function shall be small and fast. > >****************************************************************** > > > > I am trying to set up the Atari ST(e), programming in C, so that I can set > > > one of the system timers, then have the timer interrupt and call a > > function > > > when the time is up. > >This is the way to do it on regular TOS, but with FreeMiNT and most operating systems (Linux, netBSD etc), you can't set your own interrupt handler so beware what you're going to do is not clean. > > > > Is this possible and can anybody help with information on the timers and > > > necessary functions. > >Of course it is :-) The Atari has 4 hardware times, 2 to 3 of which are >reserved. The free timer is timer B. But it's bad to make assumptions on the hardware. you'd better use OS functions otherwize your program may not work on Milan > >in the TOS, there are several things you can use as timers, depending on what resolution you need. >- if you need something to occur every 50->100 times per second, you can use the VBL list. This is a list of functions that are called after the screen is refreshed. Of course the frequency depends on the refresh rate of your screen, and it's not really a timer, but it can do the job sometimes. > >- there's a function Setimer in the TOS. Although it's not portable, it's better to use it than doing dirty things on the back of the OS. > >- You can use the GEM function evnt_timer if you are in a GEM application. Note that it has a 20ms resolution though, and it's not very precise. > >- If you are using FreeMiNT (recommended, most portable method), you can use either the Talarm function, or >-better if your case, the Tsetitimer, which is precise and lets you forget about the registers of the MFP. (It also works pretty much the same as in other Unix systems). > > > > Well if you are expert in GEM you should look at vex_timv() (VDI 118) no > > doubt it will cleanly do all timer work you want. > >No, don't use it. Only the application owning the physical workstation should be able to modify these vectors, >this is a security hole in the GEM and it should not be used ! You can bet fVDI and oVDIsis will prevent this from working in the future. Also, this function is very tickelish > >The best is to use Setimer (TOS) or Tsetitimer (FreeMiNT). >For the doc of Tsetitimer, you can check the one I wrote on >http://gemtos.free.fr >To use the later reliably, you should have FreeMiNT 1.15.5 at least. > >Good luck > >Vincent > >******************************************************************* > > > timing needs improvement - I use the 200 Hz timer at the moment > > and access it through GEM). I cannot assume they have anything > > but ordinary TOS. > > > >I used to make so MIDI programming too, I made a MIDI driver for >FreeMiNT. > > > Others tell me the free timer is Timer D ! > >Well, actually, the timer D is used for the clock of the RS232, but if >you're not using the serial interface, you can use the timer D (but >don't forget to restore everything after quitting your program). > >Actually I checked the doc and it seems that it's actually Timer A which >is intened for the final user. >B is for the HBL and C is for the 200Hz system timer (used by the GEM)I >think this might be the one to use - I dont think I have seen > > > it referenced in my (limited) TOS and GEM information that I have > > found so far. Would you be able to send me the documentation for > > this function? > > > >It's void Xbtimer( int timer, int control, int data, long vector) >timer : 0=A, 1=B etc >data and control are the same as for the registers of the MFP >vector is the address of your routine > > >Ask for more if needed :-) > >Vincent > >********************************************************************* > > >The correct way is to just type the name of the function: >Xbtimer( 0, control,0xFF, do_this ); >where do_this() is the function to be called. > >******************************************************************* > > Does the timer keep counting and interrupting, or do you have to > > keep calling it? > >The timer does not rearm automatically, you have to do it yourself in your interrupt handler. > > > Next question - to recall the Xbtimer after it has been called the first > > time, can I call it again from within do_this()? Or does this cause a > > recursive loop going down? > >Xbtimer is a XBIOS function. You are not supposed to call the XBIOS from within an interrupt handler. (You can do with the BIOS, but reentrancy is limited to 2 or 3). >From what I 've seen, what people usually do is directly accessing the MFP registers from the interrupt handler to rearm the timer. > >Of course, if you use Tsetitimer you don't have these problems with registers and rearm from within the interrupt handler !! :-) >(yeah I know I'm annoying with my Tsetitimer but it is really a very good function as it you don't have to worry about saving registers, what timer to use, and what settings for the timer) > > > > .text > > _wrap: > > > > movem.l d0-d7/a0-a6,-(sp) ;save registers > > jsr _do_this ; go to do_this > > movem.l (sp)+,d0-d7/a0-a6 ; restore registers > > rte > >Mhhh, you didn't have a rte before, with your "do_this" function in C, maybe that was the real problem ? > >Vincent > >****************************************************************** > > > You will be pleased to know that I can now confidently programme my > > interrupt, using timer A, C or D (and it works!). > >woohoo :-) >still no Tsetitimer though ? ;-) > > > > However ... combining into my GEM programme is throwing up some surprises > > (of the 4-bomb variety). > >Mhhh. As far as I can remember the GEM uses the VBL as a timer, not a timer of the MFP... > > > > In my GEM programme I use an evnx_multi() which checks for user clicking > > objects, doing keyboard stuff, or menu items, for all the usual user > > interaction. > >Yes, it is usually the properway to do it. > > > > This seems to take precedence over my timer stuff - in the settings for the > > evnx_multi, there is a timer_hi and a timer_lo setting, which I set to both > > 0 otherwise the programme runs slow. > >Yeah, the VBL is on a higher interrupt level (2 I think) than the MFP (6 AFAIR), so that must be why it takes precedence. You can't do anything about that, it's wired like this. > > > > It seems my intrerrupt cannot > > interrupt into theGEM event handler? After a few minutes it invariably > > vrashes, 4 bombs. > >Well since it crashes after a determinable amount of time, this may be due to some stack problem. >If your interrupt handler can't restore the context properly before calling rte, each time it is called the system stack will be shifted, screwing the memory, and eventually reaching an illegal zone where the system complains by sending you a Segmentation Fault exception. >What may be happening in your case is that your timer keeps sending interrupts requests that can't be processed, so they get stacked until you run out of stack space. > > > > So - I made a version that once "play" is pressed, stops checking for GEM > > action, and the programme works great as it should - but of course without > > user interaction which is the whole point of the thing. > >Ok, here's what I understand: >- You have a routine running on a cyclic hardware timer interrupt. You want to allow the user to start and stop this routine using the GEM >- You use the GEM to wait for user actions using evnt_multi with no timeout, just clicks or whatever else >- Your problem is that when in evnt_multi, your routine does not run. > > >Questions: >- is your routine called at all ? >- does it resumes when event_multi returns ? > > > yet again do you have any thoughts? > >Well it seems clear that your problem is this evnt_multi blocking the execution of your program. >Something you could try is to poll for events, instead of making a blocking call. This is of course very dirty since it wastes CPU time, but since you are not using FreeMiNT it won't make a difference. >Can you try several the graf_xxxx functions instead of a single blocking event_multi ? This way you can check for mouse entering a zone, mouse buttons, and keyboard status in a non blocking way. >The drawback is that you may miss events (if you are not in the right function at the right time) and you are wasting CPU time. But under regular TOS, I can't think of any better solution right now :-( > >To be honnest, I'm surprised that you run in this kind of difficulty. It is hard to imagine that the GEM stays in the IRQ2 handler because if it did, the MFP would also never get interrupts from the keyboard ACIA and event_multi would never work. >You should check the status of the MFP after event_multi is called. What version of the AES are you using ? > >Vincent > >********************************************************************* > > > > Yes, my interrupt does get called and produces notes (within the > > event_multi) but at a speed determined by the timer setting of the > > event_multi, so obviously events are getting missed and building up. As a > > test, if I take out the timer setting fior the event_multi, then my > > interrupt is not called at all. Events happen in a string of deparate > > events whenever a mouse or keyboard action occurs - obviously control stays > > within event_multi until something happens. > >You are perfectly right. What is weird is that your routine should be in its own control (in supervisor/interrupt mode), since it is only driven by the timer, so having it blocked means the GEM is not doing something very clean. It must be masking some interrupts. > >******************************************************************* > >********** neils asm wrapper code ******************************** >****** developed by trial and error so may not be correct ! ****** > >.globl _pcspy /* this is the asm wrapper that calls my function */ >.globl _do_this /* do_this() is the midi function to be called */ >.globl _endtime >.globl _t_init > >.text >_pcspy: > > movem.l d0-d7/a0-a6,-(sp) ;save all registers > jsr _do_this ;actual code for midi stuff > > bclr #5,$fffffa07 ; timer a interrupt enable clear > bclr #5,$fffffa0b ; timer a pending clear > bclr #5,$fffffa0f ; timer a irq inservice clear > bclr #5,$fffffa13 ; timer a mask clear - crash without this > > move.b #$07,$fffffa19 ; start timer a (delay mode, divide by x) > move.b #$ff,$fffffa1f ; timer a data > ori.b #$60,$fffffa13 ; timer a mask > ori.b #$60,$fffffa07 ; timer a interrupt enable > > movem.l (sp)+,d0-d7/a0-a6 ;restore registers > rte > >_t_init: > movem.l d0-d7/a0-a6,-(sp) ;save all registers > > bclr #5,$fffffa07 ; timer a interrupt enable clear > bclr #5,$fffffa0b ; timer a pending clear > bclr #5,$fffffa0f ; timer a irq inservice clear > bclr #5,$fffffa13 ; timer a mask clear - crash without this > > > move.b #$07,$fffffa19 ; start timer a (delay mode, divide by x) > move.b #$ff,$fffffa1f ; timer a data > ori.b #$60,$fffffa13 ; timer a mask > ori.b #$60,$fffffa07 ; timer a interrupt enable > > movem.l (sp)+,d0-d7/a0-a6 ;restore registers > rte > >_endtime: > > bclr #5,$fffffa07 ; timer a interrupt enable clear > bclr #5,$fffffa0b ; timer a pending clear > bclr #5,$fffffa0f ; timer a irq inservice clear > bclr #5,$fffffa13 ; timer a mask clear - crash without this > move.b #$0,$fffffa19 ; start timer a (delay mode, divide by x) > > rte > > .end > >********************************************************************