file : /kb/timer.doc.text date : 08-Feb-1983 kb This document describes the system interface of the TIMER driver. The Timer driver performs 3 services. First, it maintains a table of user timer service routines. It invokes the user routines during a 50 millisecond timer interrupt. Second, it uses the VIA timer number 2 to produce a Bell sound (e.g. control G). Last, the driver will read and write the time and date clock. The first service, 50 milliseconds timer call, is setup by the Timer unitstatus routine. The serviced routines are invoked during the VIA timer #1 interrupt service procedure. This service call is not a precise 50 ms. interval call. Users who have very time critical processes must setup VIA timer #2 and monitor its activity. Timer #1 is for the exclusive use of the Timer driver. Therefore, do not use it for user controlled timing activities. The Timer driver unitstatus performs 4 table manipulation actions depending on the value of the input parameters. OS defined parameters for the unitstatus procedure are : unit number (word), buffer address (long word), and control (word) . The Timer driver's 4 actions are Create, Enable, Disable and Delete table entry. The Timer driver selects the correct action by the value passed as the control parameter. The control parameter values are 1 for Create, 2 for Delete, 3 for Disable and 4 for Enable. The remaining parameters to the procedure are passed in a parameter block, whose address is passed as the unitstatus parameter buffer address. Space in the parameter block must be made for Output values as well as Input values. The following is a description of the parameters in each of the table manipulation procedure's parameter blocks. Create table entry function takes as input an address of the user service routine (long word), the count of 50 ms. periods before interrupt service routine call (word) and a word of Flags and returns a table entry ID (word) of the entry made. The count ranges from 1 to 65,535, where a 1 value means make a call to the user service routine on every 50 millisecond interval timer interrupt. Currently, 2 bit flags are defined in the word of Flags. The first is the Continuous/One-shot flag. When it is clear the table entry is used until it is removed by a Delete call. When the flag is set the entry is used in a one-shot mode. Therefore, after the user service routine is called and it returns the associated table entry is automatically Deleted. The second flag is the Skip First Call flag. This flag tells the Timer driver to ignore the first call to be made to the corresponding user service routine when the down counter reaches 0 (zero). Since the Create operation is performed asynchronously to the interrupts generated by the free running interval timer (VIA timer #2), it is likely that the first user service routine call for this entry with this flag clear will occur in less time than the requested interval specified by the Count. Therefore, the user should use this flag when a minimum delay time of the count of interrupt periods is needed. The Create function has 3 input parameters and 1 output parameter for a total of 10 bytes, all of which must start on a even word boundary. Create Parameter block in Pascal fragment : CONST OneShot = 2; SkipFirst = 4; TYPE record UserServiceRtn : pointer; {Input parameter} Count : integer; {Input parameter} Flags : integer; {Input parameter} TableID : integer; {Output parameter} end;{record} Delete table entry procedure takes the table entry ID (word) for the entry desired and deletes that entry from the table. This table entry ID is the same value returned by the Create function. The Delete procedure has 1 input parameter and no (zero) output parameters, for a total of 2 bytes. The Disable procedure allows the user to prevent the Timer interrupt routine from calling the user's timer service routine without Deleting the user's table entry. The procedure has one input parameter, the table entry ID of the entry to disable. It has no output parameters. The total number of bytes for this procedure's parameter block is 2. The Enable procedure allows the user to restart a table entry. It clears the Disable condition and resets the down counter to it's starting condition. It can be used for either starting up a Disabled table entry or restarting a running entry before it causes a call to the service routine. This procedure has only 1 parameter, the table entry ID, for a total of 2 bytes. All the actions will place error condition codes within the IORESULT system variable. Only two error codnditions are defined. They are a) table full and b) no such entry (or invalid table entry ID). User timer service routines are called during the interrupt processing. They should take a minimal amount of time; I suggest less than 500 microseconds. Therefore, don't use them for doing any significant processing. They would be best used for setting flags informing the program that a timeout event has occurred. The table operations are not indivisible. Therefore, a user timer service routine may be called while the user is making a Delete or Disable call. Consequently, any operation done by the user service routine must be easily reversable after the table operation call. This only reinforces the need for keeping the user service routines simple with few destructive operations. All user timer service routines must return to the Timer driver interrupt routine via the RTS instruction. Also, no argument passing is available to the the user's timer service routine. For Pascal programs which want to have a user timer interrupt service routine there are a few restrictions. First, the procedure, NOT a function, to be used as the service routine must be a global procedure. Functions cannot be used as user timer service routines. The procedure must return to the Timer driver and cannot use Global GOTO's. When the table entry is created, the routine that calls the Timer driver Create function must be a resident part of the program which contains the user's service routine procedure. The second service, the Bell, is also performed by the Timer unitstatus function. The unitstatus parameters must be set as follows : Control must be set to 0 and buffer address must point to the Bell parameter block. The parameter block contains 3 parameters, Bell frequency (word), pattern of 8 on and offs of the speaker (byte), a filler byte to keep block to even number of bytes, and the duration in counts of Timer #1 interrupt periods (50 milliseconds), a word. The Bell parameter block is 6 bytes. The pattern of 8 on and offs of the speaker is a byte with a bit equal to 1 for speaker on and 0 for speaker off. The Timer driver uses VIA timer #2 non-exclusively. The third service, the time and date clock, is accessed via the Unitread and Unitwrite driver interfaces. The clock interfaces require a Clock Parameter Block. This is a fixed length, fixed format interface. Unlike other drivers, the timer driver cannot do reads and writes of variable length data strings. The Clock Parameter Block is one field longer for the Unitwrite procedure then for the Unitread procedure. The following is a Pascal fragment which describes the Clock Parameter Block. type clockPB = record DayofWeek : 1..7; Month : 1..12; Day : 1..31; Hour : 0..23; Mins : 0..59; Secs : 0..59; Tenths : 0..9; case INTEGER of 0 : ( {Write only} LeapYear : 0..3); end;{variant} end;{record} Though each field is a sub range of the integer type and could fit in less space then a word each field MUST be a word long. Therefore, declare each field as an integer, but restrict your useage to the valid range. The driver does validate the range in case of a user error. Unfortunately, the Day parameter is not completely verified. If the month specified is Febraury (Month := 2), but the Day is 30, the driver does not report an error. Except for the fields DayofWeek, Tenths, and LeapYear the field meanings are self-explanatory. DayofWeek specifies which day it is, such as Monday, Tuesday, etc. The value 1 specifies Sunday while 7 specifies Saturday. The field Tenths is tenths of a second. Finally, the clock has no register set for years. However, to keep the correct date after a year change the clock tracks if this year is a leap year. The LeapYear field specifies the number of years AFTER the last leap year. Therefore, if this year is leap year then the field should be assigned to 0 (zero). Consequently, if last year was leap year then the field should be assigned to 1 (one), and so forth. The LeapYear field is only used when setting the clock (Unitwrite). It is not returned when reading the clock (Unitread). To set the clock, first fill in all the fields in the Clock Parameter Block, including the LeapYear field. Next, call the timer driver Unitwrite to set the clock. Unitwrite( TimerDriverNumber, WriteParmBlock, LengthWriteParmBlock ); The driver will set the IORESULT variable if the parameter block is in error. To read the clock just call the timer driver Unitread with the Clock Parameter Block. Unitread( TimerDriverNumber, ReadParmBlock, LengthReadParmBlock ); The correct length of the Read version of the Clock Paramter Block must be passed to the driver. If it is to short, an error is returned in IORESULT and the clock is NOT read. The Timer driver unitinstall procedure sets-up the VIA, initializes the timer table to no entries value, starts the 50 ms. interrupt driven interval timer (VIA timer #1), and initializes the clock chip. Clock initialization makes sure the clock is running, the chip is not in test mode, and the interrupt capabilities of the chip are reset. The chip can interrupt, however, it is not connected to the system interrupt system and it's interrupt is ignored. The interrupt is cleared and reset so it does not effect the running of the clock. The Timer driver unitunmount procedure turns off the VIA timer interrupt for Timer #2. It also installs in the timer interrupt vector a pointer to a ReTurn from Exception (RTE) instruction to insure system integrity in case of a spurrious level 5 interrupt.