/* * Cia_Interval.c * * Demonstrate allocation and use of a cia interval timer * * Compile with SAS C 5.10 lc -b1 -cfistq -v -y -L * * Run from CLI only */ #include <exec/types.h> #include <exec/memory.h> #include <exec/tasks.h> #include <exec/interrupts.h> #include <hardware/cia.h> #include <resources/cia.h> #include <clib/exec_protos.h> #include <clib/cia_protos.h> #include <stdlib.h> #include <stdio.h> #include <string.h> /* prototypes */ void StartTimer (struct freetimer *ft, struct exampledata *ed); int FindFreeTimer (struct freetimer *ft, int preferA); int TryTimer (struct freetimer *ft); void main ( USHORT, char **); /* see usage of these defines in StartTimer() below */ #define COUNTDOWN 20 #define HICOUNT 0xFF #define LOCOUNT 0xFF #define STOPA_AND CIACRAF_TODIN |CIACRAF_PBON | CIACRAF_OUTMODE | CIACRAF_SPMODE /* ; ; AND mask for use with control register A ; (interval timer A on either CIA) ; ; STOP - ; START bit 0 == 0 (STOP IMMEDIATELY) ; PBON bit 1 == same ; OUT bit 2 == same ; RUN bit 3 == 0 (SET CONTINUOUS MODE) ; LOAD bit 4 == 0 (NO FORCE LOAD) ; IN bit 5 == 0 (COUNTS 02 PULSES) ; SP bit 6 == same ; TODIN bit 7 == same (unused on ciacra) */ #define STOPB_AND CIACRBF_ALARM | CIACRBF_PBON | CIACRBF_OUTMODE /* ; ; AND mask for use with control register B ; (interval timer B on either CIA) ; ; STOP - ; START bit 0 == 0 (STOP IMMEDIATELY) ; PBON bit 1 == same ; OUT bit 2 == same ; RUN bit 3 == 0 (SET CONTINUOUS MODE) ; LOAD bit 4 == 0 (NO FORCE LOAD) ; IN0 bit 5 == 0 (COUNTS 02 PULSES) ; IN1 bit 6 == 0 (COUNTS 02 PULSES) ; ALARM bit 7 == same (TOD alarm control bit) */ #define STARTA_OR CIACRAF_START /* ; ; OR mask for use with control register A ; (interval timer A on either CIA) ; ; START - ; ; START bit 0 == 1 (START TIMER) ; ; All other bits unaffected. ; */ #define STARTB_OR CIACRBF_START /* ; ; OR mask for use with control register B ; (interval timer A on either CIA) ; ; START - ; ; START bit 0 == 1 (START TIMER) ; ; All other bits unaffected. ; */ /* * Structure which will be used to hold all relevant information about * the cia timer we manage to allocate. * */ struct freetimer { struct Library *ciabase; /* CIA Library Base */ ULONG timerbit; /* timer bit allocated */ struct CIA *cia; /* ptr to hardware */ UBYTE *ciacr; /* ptr to control register */ UBYTE *cialo; /* ptr to low byte of timer */ UBYTE *ciahi; /* ptr to high byte of timer */ struct Interrupt timerint; /* Interrupt structure */ UBYTE stopmask; /* Stop/set-up timer */ UBYTE startmask; /* Start timer */ }; /* * Structure which will be used by the interrupt routine called * when our cia interval timer generates an interrupt. * */ struct exampledata { struct Task *task; /* task to signal */ ULONG signal; /* Signal bit to use */ ULONG counter; }; struct CIA *ciaa = (struct CIA *)0xbfe001; struct CIA *ciab = (struct CIA *)0xbfd000; #ifdef LATTICE int CXBRK(void) { return(0); } /* Disable SAS CTRL/C handling */ int chkabort(void) { return(0); } /* really */ #endif /* * This is the interrupt routine which will be called when our CIA * interval timer counts down. * * This example decrements a counter each time the interrupt routine * is called until the counter reaches 0, at which time it signals * our main task. * * Note that interrupt handling code should be efficient, and will * generally be written in assembly code. Signaling another task * such as this example does is also a useful way of handling * interrupts in an expedient manner. */ void __asm ExampleInterrupt(register __a1 struct exampledata *ed) { if (ed->counter) { ed->counter--; /* decrement counter */ } else { ed->counter = COUNTDOWN; /* reset counter */ Signal(ed->task,(1L << ed->signal)); } } /*********************************** * main() ***********************************/ void main(USHORT argc,char **argv) { struct freetimer ft; struct exampledata ed; /* Set up data which will be passed to interrupt */ ed.task = FindTask(0L); if (ed.signal = AllocSignal(-1L)) { /* Prepare freetimer structure : set-up interrupt */ ft.timerint.is_Node.ln_Type = NT_INTERRUPT; ft.timerint.is_Node.ln_Pri = 0; ft.timerint.is_Node.ln_Name = "cia_example"; ft.timerint.is_Data = (APTR)&ed; ft.timerint.is_Code = (APTR)ExampleInterrupt; /* Call function to find a free CIA interval timer * with flag indicating that we prefer a CIA-A timer. */ printf("Attempting to allocate a free timer\n"); if (FindFreeTimer(&ft,TRUE)) { if (ft.cia == ciaa) { printf("CIA-A timer "); } else { printf("CIA-B timer "); } if (ft.timerbit == CIAICRB_TA) { printf("A allocated\n"); } else { printf("B allocated\n"); } /* We found a free interval timer. Let's start it running. */ StartTimer(&ft,&ed); /* Wait for a signal */ printf("Waiting for signal bit %ld\n",ed.signal); Wait(1L<<ed.signal); printf("We woke up!\n"); /* Release the interval timer */ RemICRVector(ft.ciabase,ft.timerbit,&ft.timerint); } else { printf("No CIA interval timer available\n"); } FreeSignal(ed.signal); } } /* * This routine sets up the interval timer we allocated with * AddICRVector(). Note that we may have already received one, or * more interrupts from our timer. Make no assumptions about the * initial state of any of the hardware registers we will be using. * */ void StartTimer(struct freetimer *ft, struct exampledata *ed) { register struct CIA *cia; cia = ft->cia; /* Note that there are differences between control register A, * and B on each CIA (e.g., the TOD alarm bit, and INMODE bits. */ if (ft->timerbit == CIAICRB_TA) { ft->ciacr = &cia->ciacra; /* control register A */ ft->cialo = &cia->ciatalo; /* low byte counter */ ft->ciahi = &cia->ciatahi; /* high byte counter */ ft->stopmask = STOPA_AND; /* set-up mask values */ ft->startmask = STARTA_OR; } else { ft->ciacr = &cia->ciacrb; /* control register B */ ft->cialo = &cia->ciatblo; /* low byte counter */ ft->ciahi = &cia->ciatbhi; /* high byte counter */ ft->stopmask = STOPB_AND; /* set-up mask values */ ft->startmask = STARTB_OR; } /* Modify control register within Disable(). This is done to avoid * race conditions since our compiler may generate code such as: * * value = Read hardware byte * AND value with MASK * Write value to hardware byte * * If we take a task switch in the middle of this sequence, two tasks * trying to modify the same register could trash each others' bits. * * Normally this code would be written in assembly language using atomic * instructions so that the Disable() would not be needed. */ Disable(); /* STOP timer, set 02 pulse count-down mode, set continuous mode */ *ft->ciacr &= ft->stopmask; Enable(); /* Clear signal bit - interrupt will signal us later */ SetSignal(0L,1L<<ed->signal); /* Count-down X # of times */ ed->counter = COUNTDOWN; /* Start the interval timer - we will start the counter after * writing the low, and high byte counter values */ *ft->cialo = LOCOUNT; *ft->ciahi = HICOUNT; /* Turn on start bit - same bit for both A, and B control regs */ Disable(); *ft->ciacr |= ft->startmask; Enable(); } /* * A routine to find a free interval timer. * * This routine makes no assumptions about which interval timers * (if any) are available for use. Currently there are two interval * timers per CIA chip. * * Because CIA usage may change in the future, your code should use * a routine like this to find a free interval timer. * * Note that the routine takes a preference flag (which is used to * to indicate that you would prefer an interval timer on CIA-A). * If the flag is FALSE, it means that you would prefer an interval * timer on CIA-B. * */ FindFreeTimer(struct freetimer *ft, int preferA) { struct CIABase *ciaabase, *ciabbase; /* get pointers to both resource bases */ ciaabase = OpenResource(CIAANAME); ciabbase = OpenResource(CIABNAME); /* try for a CIA-A timer first ? */ if (preferA) { ft->ciabase = ciaabase; /* library address */ ft->cia = ciaa; /* hardware address */ } else { ft->ciabase = ciabbase; /* library address */ ft->cia = ciab; /* hardware address */ } if (TryTimer(ft)) return(TRUE); /* try for an interval timer on the other cia */ if (!(preferA)) { ft->ciabase = ciaabase; /* library address */ ft->cia = ciaa; /* hardware address */ } else { ft->ciabase = ciabbase; /* library address */ ft->cia = ciab; /* hardware address */ } if (TryTimer(ft)) return(TRUE); return(FALSE); } /* * Try to obtain a free interval timer on a CIA. */ TryTimer(struct freetimer *ft) { if (!(AddICRVector(ft->ciabase,CIAICRB_TA,&ft->timerint))) { ft->timerbit = CIAICRB_TA; return(TRUE); } if (!(AddICRVector(ft->ciabase,CIAICRB_TB,&ft->timerint))) { ft->timerbit = CIAICRB_TB; return(TRUE); } return(FALSE); }