home *** CD-ROM | disk | FTP | other *** search
/ Amiga Developer CD v1.2 / amidev_cd_12.iso / reference / amiga_mail_vol1 / serparcia / timercia < prev    next >
Text File  |  1990-01-26  |  12KB  |  417 lines

  1. (c)  Copyright 1989 Commodore-Amiga, Inc.   All rights reserved.
  2. The information contained herein is subject to change without notice, and 
  3. is provided "as is" without warranty of any kind, either expressed or implied.  
  4. The entire risk as to the use of this information is assumed by the user.
  5.  
  6.  
  7.  
  8.             A Timer Using the CIA Resource
  9.  
  10.          by Adam Levin and Paul Higginbottom
  11.  
  12.  
  13. The Amiga has two identical Complex Interface Adaptor (CIA) chips within it
  14. which help it do many things; from communicating with the outside world
  15. through the serial and parallel ports to keeping track of time.
  16. The CIA is capable of carrying out some of these duties entirely on its own,
  17. which helps the Amiga run at top speed.  For example, it is possible to program
  18. a CIA chip to count from a given number down to zero and inform the
  19. Central Processing Unit (CPU) when it has finished.  The CIA can inform the
  20. CPU by means of an interrupt.  This is a hardware signal which causes the
  21. CPU to pause what it was doing, perform a different task, and then resume
  22. right where it left off.  So a clock program, for example, rather than
  23. knowing what a tenth of a second is, could simply tell the CIA to interrupt
  24. it every tenth of a second at which point it would update the time.
  25.  
  26. The accompanying program ("Timer") makes working with regular intervals of
  27. time very easy.  Your program simply tells Timer how many microseconds it 
  28. wants between interrupts.  It can then either wait for each interrupt, and 
  29. execute its task at that time or work at some task continuously, and check 
  30. the value of a special variable to see how many units of time have passed.
  31.  
  32.  
  33.  
  34. Using The Timer Program
  35.  
  36. To use the Timer program, follow these steps:
  37.  
  38. Tell the routine how many microseconds there are in your unit of time
  39. (TIME_SLICE) with SetTimer().
  40.  
  41. Start the timing with BeginTimer().  A return value of TRUE means the
  42. timing has begun.  A return value of FALSE means that the timer is
  43. unavailable now - the timer or the interrupt vector may already be in use. 
  44. Your routine is still responsible for calling EndTimer().
  45.  
  46. Every TIME_SLICE microseconds, the value of the external variable "Timer"
  47. will be incremented.  Your program can check "Timer" to find out how many
  48. units of time have passed.  Your program can set "Timer" to zero or another
  49. convenient value at any point.
  50.  
  51. In addition, you can specifically wait for any or all interrupts to occur
  52. with Wait().  The example program, main, shows how to set up and wait for
  53. each interrupt.  It is important to keep in mind that the task performed
  54. after an interrupt may take longer than the next interval of time.  If your
  55. program checks the value of "Timer" immediately before the task and
  56. immediately after; it can tell how many intervals have gone by.
  57.  
  58. Timer will compile and run using either the Lattice or Manx C compilers.
  59. The code which is specific to one compiler or the other has been enclosed in
  60. #ifdef/#ifndef/#endif statements to ensure that the correct compiler gets
  61. the correct code.
  62.  
  63.  
  64.  
  65. Timer Limitations
  66.  
  67. Only ONE of the many programs that may be running in the Amiga's multi-
  68. tasking environment can use the CIA resource.  Once the resource has been 
  69. opened, no one else can use it until it is closed.  Because of this, less 
  70. demanding applications should use the timer.device which fully supports 
  71. multi-tasking.  The drawback of the timer.device is that it loses accuracy 
  72. as system load increases.
  73.  
  74. The value set for TIME_SLICE will not give you an exact number of microseconds.
  75. This is true because the CIA chip is run at one-tenth the speed of the CPU,
  76. or about 716 KHz.  Multiply the desired number of microseconds by 1.397
  77. to get the value for TIME_SLICE.  Also note that the external variable "Timer"
  78. is an unsigned short, so it can only hold 65,535 counts before it wraps around
  79. to zero.
  80.  
  81.  
  82.  
  83. Allocation of CIA Timers
  84.  
  85. The example timer program shown below uses Timer B of CIA-B to provide the
  86. clock tick since this is the only timer not reserved for the system.
  87. There are a total of six CIA timers all together, but five of these are
  88. reserved.  The allocation of the CIA timers is as follows: 
  89.  
  90.  
  91. CIA Timer Allocations
  92.  
  93. CIA A - Interrupt 2
  94.  
  95.     Timer A     Used for keyboard handshake
  96.     Timer B        Used for uSec timer.device
  97.     TOD        Used for 50/60 Hz timer.device
  98.  
  99. CIA B - Interrupt 6
  100.  
  101.     Timer A        Commodore 8-bit serial bus communication
  102.     Timer B        Not used
  103.     TOD        Used for graphics.library beam counter
  104.  
  105.  
  106. As you can see, all the timers on CIA-A are reserved for system use.  Do not 
  107. use CIA-A in your applications.  Instead use Timer B of CIA-B.  This is not 
  108. used by the system at all.  
  109.  
  110. You could also use Timer A of CIA-B.  Officially, this is reserved for a 
  111. "1541-style" interface for products like the C64-Emulator.  In practice, it 
  112. is almost never used for this purpose.
  113.  
  114. It is important to note that the CIA timer allocations shown in Appendix F of 
  115. the Hardware Manual are wrong.  Both the Addison-Wesley and Commodore versions
  116. have mistakes.  The table above gives the correct allocations.  
  117.  
  118. The example program listed below originally appeared in the September-October 
  119. 1988 issue of Amiga Mail.  In the original version, which was written "by the 
  120. book", the wrong timer was used because of the error in the Hardware Manuals.
  121. The version shown here is corrected and uses CIA-B, Timer B.
  122.  
  123. Likewise, the CIA program from Fred Fish Disk #178 by Karl Lehenbauer and Paul
  124. Higginbottom also uses the wrong timer.  If you are doing work based on the
  125. Fish Disk version, be sure to change the timer used to CIA-B, Timer B.  
  126.  
  127. Time-critical applications can use direct control of the CIA resources to get 
  128. a very accurate clock signal with low overhead.  However, programmers must be 
  129. careful to use the right CIA timer.  For applications CIA-B, Timer B should 
  130. be used.  For more about the CIAs, see Appendix F of the Addison-Wesley
  131. Hardware Manual.
  132.  
  133.  
  134. ---------------------- code starts here ------------------------------
  135.  
  136. /*
  137.    TIMER - Amiga CIA Timer Control Software
  138.    v1.1
  139.    Written by Paul Higginbottom.
  140.    Placed in the Public Domain.
  141.    Manx make: cc timer.c
  142.               ln timer.o -lc
  143. */
  144.  
  145. #include <exec/types.h>
  146. #include <exec/tasks.h>
  147. #include <exec/interrupts.h>
  148. #include <hardware/custom.h>
  149. #include <hardware/intbits.h>
  150. #include <hardware/cia.h>
  151. #include <resources/cia.h>
  152. #include <stdio.h>
  153.  
  154. /* Manx's C defines ciab in <hardware/cia.h> */
  155. #ifndef AZTEC_C
  156. extern struct CIA ciab;
  157. #endif
  158.  
  159. /* Set DEBUG to a non-zero value if you want debugging. */
  160. #define DEBUG 0
  161.  
  162. /*
  163.    Globals: Other files can use these.
  164. */
  165. int              TimerSigBit = -1;   /* allocated signal bit */
  166. long             TimerSigMask;       /* TimerSigBit converted into a mask */
  167. unsigned short   Timer;              /* CIA timer underflow clock */
  168.  
  169. /*
  170.    Statics: Only this file need know about these.
  171. */
  172. static struct Interrupt
  173.    TimerInterrupt,               /* The interrupt structure */
  174.    /*
  175.       OldCIAInterrupt must be non-null to ensure that EndTimer() only
  176.       removes the interrupt if it was properly installed by BeginTimer().
  177.    */
  178.    *OldCIAInterrupt = (struct Interrupt *)-1;
  179. static struct Library *CIAResource = NULL;
  180. static struct Task *thisTask;
  181.  
  182. /*
  183.    These defines make the code look a little more readable.
  184. */
  185. #define ciatlo      ciab.ciatblo
  186. #define ciathi      ciab.ciatbhi
  187. #define ciacr       ciab.ciacrb
  188. #define CIAINTBIT   CIAICRB_TB
  189. #define CLEAR       0
  190.  
  191.  
  192. /*
  193.    Start the timer, clear pending interrupts, and enable timer B interrupts.
  194. */
  195. void
  196. StartTimer()
  197. {
  198.    void SetICR(), AbleICR();
  199.  
  200.    ciacr &= ~CIACRBF_RUNMODE;   /* Set it to reload upon underflow. */
  201.    ciacr |= CIACRBF_LOAD | CIACRBF_START;   /* Load and start. */
  202.    SetICR(CIAResource, CLEAR | CIAICRF_TB);
  203.    AbleICR(CIAResource, CIAICRF_SETCLR | CIAICRF_TB);
  204. }
  205.  
  206. /*
  207.    Stop the timer.  Disable timer B interrupts first.
  208. */
  209. void
  210. StopTimer()
  211. {
  212.    void AbleICR();
  213.  
  214.    AbleICR(CIAResource, CLEAR | CIAICRF_TB);
  215.    ciacr &= ~CIACRBF_START;
  216. /*
  217.    Set specific period between Timer increments.
  218. */
  219. void
  220. SetTimer(micros)
  221. unsigned short micros;
  222. {
  223.    ciatlo = micros & 0xff;
  224.    ciathi = micros >> 8;
  225. }
  226.  
  227.  
  228. /*
  229.    Initialize interrupt structure, allocate a signal and start
  230.    the timer.  If all goes well it returns TRUE, otherwise it
  231.    returns FALSE.  You must call EndTimer() to clean up regardless
  232.    of the return value.
  233. */
  234. BOOL
  235. BeginTimer()
  236. {
  237.    extern long AllocSignal();
  238.    extern void TimeOut();
  239.    extern struct Library *OpenResource();
  240.    extern struct Interrupt *AddICRVector();
  241.    extern struct Task *FindTask();
  242.  
  243.    thisTask = FindTask(NULL);
  244.  
  245.    /*
  246.       Get a signal bit.
  247.    */
  248.    if ((TimerSigBit = AllocSignal(-1L)) == -1)
  249.    {
  250. #if DEBUG
  251.       puts("Timer: AllocSignal failed.");
  252. #endif
  253.       return(FALSE);
  254.    }
  255.    TimerSigMask = 1L << TimerSigBit;
  256.    /*
  257.       Open the CIA resource
  258.    */
  259.    if ((CIAResource = OpenResource(CIABNAME)) == NULL)
  260.    {
  261. #if DEBUG
  262.       printf("Timer: Couldn't open %s.\n", CIABNAME);
  263. #endif
  264.       return(FALSE);
  265.    }
  266.    /*
  267.       Initialize the interrupt structure.
  268.    */
  269.    TimerInterrupt.is_Node.ln_Type = NT_INTERRUPT;
  270.    TimerInterrupt.is_Node.ln_Pri = 127;
  271.  
  272. #ifdef AZTEC_C
  273. #asm
  274. ;   Use machine code to set is_Data field of interrupt
  275. ;   structure to the contents of the a4 register.
  276.  
  277.    include "exec/types.i"
  278.    include "exec/interrupts.i"
  279.  
  280.    lea      _TimerInterrupt,a0   ; Set up interrupt's data pointer as
  281.    move.l   a4,IS_DATA(a0)       ; pointer to Manx's data segment.
  282. #endasm
  283. #endif
  284.  
  285.    TimerInterrupt.is_Code = TimeOut;
  286.  
  287.    /*
  288.       Install the interrupt code.
  289.    */
  290.    if ((OldCIAInterrupt =
  291.          AddICRVector(CIAResource, CIAINTBIT, &TimerInterrupt)) != NULL)
  292.    {
  293. #if DEBUG
  294.       puts("Timer: Interrupt in use.");
  295. #endif
  296.       return(FALSE);
  297.    }
  298.    StartTimer();
  299.    return(TRUE);
  300. }
  301.  
  302. /*
  303.    Stop the timer and remove it's interrupt vector.
  304. */
  305. void
  306. EndTimer()
  307. {
  308.    if (TimerSigBit != -1)
  309.    {
  310.       if (OldCIAInterrupt == NULL)
  311.       {
  312.          StopTimer();
  313.          RemICRVector(CIAResource, CIAINTBIT, &TimerInterrupt);
  314.       }
  315.       FreeSignal(TimerSigBit);
  316.    }
  317. }
  318.  
  319. #ifdef AZTEC_C
  320. #asm
  321. ;   Timer Interrupt handler.
  322. ;   Increment Timer variable upon timer underflow
  323.  
  324.    public   _Timer,_LVOSignal,_thisTask
  325.    public   _TimeOut
  326.  
  327. _TimeOut:
  328.    move.l   a4,a5               ; Save a4; get Manx data segment
  329.    move.l   a1,a4               ; (IS_DATA is passed to interrupt in A1).
  330.    addq.w   #1,_Timer           ; Increment timer.
  331.    move.l   _TimerSigMask,d0    ; Put arguments in registers
  332.    move.l   _thisTask,a1        ; in preparation for call to
  333.    jsr      _LVOSignal(a6)      ; LVOSignal.
  334.    move.l   a5,a4               ; Restore a4.
  335.    rts
  336. #endasm
  337. #else
  338. void TimeOut()
  339. {
  340.    ++Timer;
  341.    Signal(thisTask,TimerSigMask);
  342. }
  343. #endif
  344.  
  345. /*
  346.      E N D   O F   T I M E R   R O U T I N E S
  347.  
  348. (What follows is a program to exercise the timer routines).
  349.  
  350. */
  351.  
  352.  
  353.  
  354.  
  355.  
  356.  
  357. /*
  358.   DoSomething - "Noise-making" function called by main (demo) program.
  359. */
  360. void
  361. DoSomething()
  362. {
  363.    int i;
  364.  
  365.    if ((Timer % 100) == 0) 
  366.    {
  367.       for (i = 0; i < Timer / 100; ++i)
  368.       {
  369.          printf(" ");
  370.       }
  371.       printf("clickety\n");
  372.    }
  373. }
  374.  
  375. /*
  376.    MAIN - Demo program to exercise "Timer" Timer Control Software.
  377.    Written by Paul Higginbottom.  Placed in the Public Domain.
  378. */
  379.  
  380. #include <exec/types.h>
  381. /*
  382.    Update Timer every TIME_SLICE microseconds.
  383. */
  384. #define TIME_SLICE  ((unsigned short) 10000)
  385.  
  386. main()
  387. {
  388.    extern BOOL BeginTimer();
  389.    extern unsigned short Timer;
  390.    extern long TimerSigMask;
  391.  
  392. #ifdef AZTEC_C
  393.    extern short Enable_Abort;
  394.    Enable_Abort = 0;   /* No CTRL-C automatic break-outs allowed. */
  395. #else
  396.    /*  Disable Lattice CTRL-C handling via the method provided
  397.        in your release.
  398.    */
  399. #endif
  400.  
  401.    SetTimer(TIME_SLICE);   /* Tell the timer the size of a time unit. */
  402.    Timer = 0;
  403.    if (BeginTimer())   /* Try to start the ball rolling. */
  404.    {
  405.       do
  406.       {
  407.           Wait(TimerSigMask);   /* Wait for click. */
  408.           DoSomething();
  409.       } while (Timer < 1000);
  410.    }
  411.    EndTimer();         /* It's a wrap. */
  412. }
  413.  
  414.  
  415.  
  416.