====== The Timer Device ====== ---- The timer device is a software component that provides a standardized interface to the timing hardware.The 3DO hardware architecture includes a number of clocks and timers. The timer device has two separate timer units that offer different timing characteristics: the microsecond unit (''%%TIMER_UNIT_USEC%%'') and the vertical blank unit (''%%TIMER_UNIT_VBLANK%%''). The microsecond timer deals with time quantities using seconds and microseconds, while the vertical blank timer counts time in vertical blank intervals. Vertical blank intervals are discussed in more detail in [[:documentation:development:opera:pf25:ppgfldr:pgsfldr:spg:#xref22584|The Vertical Blank Unit]]. Both units respond to the same commands. Using either of the timer units, you can do four basic operations: * Sample the current time * Ask the timer to notify you when a given time arrives * Ask the timer to notify you after a given amount of time passes * Ask the timer to notify you at regular intervals until you ask it to stop The following sections describe how to perform these operations with either timer device units. **Note:** The timer device currently doesn't provide real-time clocks. That is, the timer starts counting time only when the machine is turned on, it stops counting time when the machine is shut down. Therefore, it is not currently possible to automatically determine the current time of day. This capability may be added to the 3DO architecture in the future. ===== Working With the Timer Device ===== Communicating with the two units of the timer device is done using the standard Portfolio I/O commands. To send commands to the timer device, you must complete the following steps: - Open the timer device using the ''%%OpenNamedDevice()%%'' function. - Create an IOReq structure by calling the ''%%CreateIOReq()%%'' function. - Initialize an IOInfo structure that specifies the command and parameters to the command. - Send the command to the timer device using either ''%%DoIO()%%'' or ''%%SendIO().%%'' ===== The Microsecond Unit ===== The microsecond timer unit provides very high-resolution timing. Although it has very high short-term accuracy, its time base can drift slightly over extended periods of time. This microsecond unit of the timer device deals in time quantities using the TimeVal structure, which is defined as: ''%%%%'' typedef struct timeval { int32 tv_Seconds; /* seconds */ int32 tv_Microseconds; /* and microseconds */ } TimeVal; ==== Reading the Current System Time ==== To read the current system time, you must initialize an IOInfo structure such as: ''%%%%'' IOInfo ioInfo; TimeVal tv; memset(&ioInfo,0,sizeof(ioInfo)); ioInfo.ioi_Command = CMD_READ ioInfo.ioi_Unit = TIMER_UNIT_USEC; ioInfo.ioi_Recv.iob_Buffer = &tv; ioInfo.ioi_Recv.iob_Len = sizeof(tv); The ''%%ioi_Command%%'' field is set to ''%%CMD_READ,%%'' indicating the current system time should be read. ''%%ioi_Unit%%'' indicates which unit of the timer device to query. ''%%ioi_Recv.iob_Buffer%%'' points to a TimeVal structure. This pointer is where the timer device stores the current time value. Finally, ''%%ioi_Recv.iob_Len%%'' holds the size of the TimeVal structure. Once an I/O operation is performed on the timer device using the above ''%%IOInfo%%'', the timer device puts the current system time in the supplied TimeVal structure, and completes the I/O operation by sending your task a message or a signal, depending on how the I/O request was created. For a high performance way to read the system's microseconds clock, see [[:documentation:development:opera:pf25:ppgfldr:pgsfldr:spg:#xref26630|High-Performance Timing]]. ==== Waiting for a Specific Time to Arrive ==== A common use for the timer device is to provide automatic notification of the passage of time. For example, if you want a given picture to remain on the display for exactly 1 second, you can send a command to the timer device telling it to send you a signal when 1 second has passed. While you are waiting for that second to pass, your task can do other work, such as play music, confident in the fact that the timer device will notify it when the appropriate amount of time has passed. You can ask the timer device to notify you when a specific time arrives. To do this, you must first ask the system what the current time is by sending the device a ''%%CMD_READ%%''. Once you know the current time, you can use the ''%%AddTimes()%%'' and ''%%SubTimes()%%'' calls, explained below, to calculate the time to receive a notification. Once you have calculated the time to be notified, you can send the ''%%TIMERCMD_DELAYUNTIL%%'' command to the timer device. You must initialize the IOInfo structure in the following way: ''%%%%'' IOInfo ioInfo; TimeVal tv; memset(&ioInfo,0,sizeof(ioInfo)); ioInfo.ioi_Command = TIMERCMD_DELAYUNTIL; ioInfo.ioi_Unit = TIMER_UNIT_USEC; ioInfo.ioi_Send.iob_Buffer = &tv; ioInfo.ioi_Send.iob_Len = sizeof(tv); The ''%%ioi_Comman%%''d field is set to ''%%TIMERCMD_DELAYUNTIL, %%''indicating that the timer will wait until a specific time arrives. ''%%ioi_Unit%%'' indicates which unit of the timer device to use. ''%%ioi_Send.iob_Buffer%%'' points to a TimeVal structure. This contains the amount of time to wait. Finally, ''%%ioi_Send.iob_Le%%''n holds the size of the TimeVal structure. You can send the I/O request to the timer device using either ''%%DoIO()%%'' or ''%%SendIO()%%''. When using ''%%DoIO(),%%'' your task is put to sleep until the requested time. If you use ''%%SendIO()%%'', then your task is free to continue working while the timer is counting time. When the requested time arrives, the timer device will either send your task the ''%%SIGF_IODONE%%'' signal, or will send you a message as specified in your I/O request. ==== Waiting a Specific Amount of Time ==== Instead of asking the timer to wait until a given time, you can tell it to wait for a fixed amount of time to pass. To achieve this, you follow the procedure in the previous section except that you initialize the IOInfo structure differently. For a specific amount of time, ''%%ioi_Command%%'' must be set to ''%%TIMERCMD_DELAY%%'', and the TimeVal structure you supply must specify an amount of time instead of a certain time. ==== Getting Repeated Notifications ==== Often, it is desirable to have a timer automatically generate a signal in regular fixed intervals. The ''%%TIMERCMD_METRONOME%%'' commands arranges to have a signal sent to your task for an undetermined length of time at a fixed rate. ''%%%%'' IOInfo ioInfo; TimeVal tv; int32 signals; memset(&ioInfo,0,sizeof(ioInfo)); ioInfo.ioi_Command = TIMERCMD_METRONOME; ioInfo.ioi_Unit = TIMER_UNIT_USEC; ioInfo.ioi_CmdOptions = signals; ioInfo.ioi_Send.iob_Buffer = &tv; ioInfo.ioi_Send.iob_Len = sizeof(tv); The ''%%ioi_Comman%%''d field is set to ''%%TIMERCMD_METRONOME%%'' indicating that the timer acts as a metronome, and sends your task signals every time a specified amount of time passes. ''%%ioi_Unit%%'' indicates which unit of the timer device to use. ''%%ioi_CmdOptions%%'' specifies the signal mask that the timer device should use when signalling your task. ''%%ioi_Send.iob_Buffer%%'' points to a TimeVal structure. This contains the amount of time between each signal that the timer device sends your task. Finally, ''%%ioi_Send.iob_Le%%''n holds the size of the TimeVal structure. Send the I/O request to the timer device using ''%%SendIO()%%''. Once this is done, the timer device will send a signal to your task every time the specified amount of time passes. To stop the timer device from sending these signals, you must abort the I/O request using the''%% AbortIO()%%'' call. ===== The Vertical Blank Unit ===== The vertical blank timer unit provides a fairly coarse measure of time, but is very stable over long periods of time. It offers a resolution of either 1/60th of a second on NTSC systems or 1/50th of a second on PAL systems. Vertical blanking is a characteristic of raster scan displays, and occurs on a fixed time-scale synchronized with the display hardware. A //vblank// is the amount of time it takes for the video beam to perform an entire sweep of the display. Given that displays operate at different refresh rates in NTSC (60 Hz) compared to PAL (50 Hz), the amount of time taken by a vblank varies. Since the vblank unit of the timer device deals with time exclusively in terms of vblank units, waiting for a fixed number of vblanks will take different amounts of time on NTSC and PAL. The advantages of the vertical blank timer are that it remains stable for very long periods of time; it involves slightly less overhead than the microsecond unit; and it is synchronized with the video beam. Being synchronized with the video beam is very important when creating animation sequences. This vertical blank unit of the timer device deals in time quantities using the VBlankTimeVal structure, which is defined as: ''%%%%'' typedef struct VBlankTimeVal { uint32 vbltv_VBlankHi32; * upper 32 bits of vblank counter */ uint32 vbltv_VBlankLo32; /* lower 32 bits of vblank counter */ } VBlankTimeVal; Vblanks are counted using a 64-bit counter. This is represented in two 32-bit words. The upper-32 bits, which are the most significant, are stored in the ''%%vbltv_VBlankHi32%%'' field, while the lower-32 bits are stored in the ''%%vbltv_VBlankLo32%%'' field. ==== Reading the Current System Time ==== To read the current system time, you must initialize an IOInfo structure such as: ''%%%%'' IOInfo ioInfo; VBlankTimeVal vbltv; ''%%%%'' memset(&ioInfo,0,sizeof(ioInfo)); ioInfo.ioi_Command = CMD_READ ioInfo.ioi_Unit = TIMER_UNIT_VBLANK; ioInfo.ioi_Recv.iob_Buffer = &vbltv; ioInfo.ioi_Recv.iob_Len = sizeof(vbltv); ''%%ioi_Command%%'' is set to ''%%CMD_READ, %%''indicating that the current system time should be read. ''%%ioi_Unit%%'' indicates which unit of the timer device to query. ''%%ioi_Recv.iob_Buffer%%'' points to a VBlankTimeVal structure. This is where the timer device stores the current vblank count. Finally, ''%%ioi_Recv.iob_Len%%'' holds the size of the VBlankTimeVal structure. Once the I/O is complete, the supplied VBlankTimeVal structure is filled with the current vblank count. Given that there are either 50 or 60 vblanks per second, over 800 days worth of vblanks can be stored in ''%%vbltv_VBlankLo32%%''. Whenever ''%%vbltv_VBlankLo32%%'' exceeds the maximum value it can contain (2^32 - 1), then the value of ''%%vbltv_VBlankHi32%%'' is incremented by 1. This means that the VBlankTimeVal structure being used to store vblank counts can hold up to (2^32 * 800) days, which is probably longer than the time remaining before the sun goes supernova. ==== Waiting for a Specific Time ==== Similar to the microsecond unit, the vblank unit can wait for a specific time. You specify this time in terms of vblanks. Unlike the microsecond unit, you do not specify the amount of time to wait using a TimeVal structure. Instead, set the''%% ioi_Offset%%'' field of the IOInfo structure to the vblank count. ''%%%%'' IOInfo ioInfo; memset(&ioInfo,0,sizeof(ioInfo)); ioInfo.ioi_Command = TIMERCMD_DELAYUNTIL; ioInfo.ioi_Unit = TIMER_UNIT_VBLANK; ioInfo.ioi_Offset = vblankCountToWaitUntil; ==== Waiting for a Specific Amount of Time ==== The vblank unit can wait for a given number of vblanks, that is, a specific amount of time. This is done by initializing an IOInfo structure such as: ''%%%%'' IOInfo ioInfo; memset(&ioInfo,0,sizeof(ioInfo)); ioInfo.ioi_Command = TIMERCMD_DELAY; ioInfo.ioi_Unit = TIMER_UNIT_VBLANK; ioInfo.ioi_Offset = numberOfVBlanksToWait; It is important to understand that the timer counts vblanks in a very strict way. Whenever the video beam reaches a known location on the display, the vblank counter is incremented. So if you ask the timer to wait for 1 vblank while the beam is near the trigger location, the I/O request will be returned in less than 1/60th or 1/50th of a second. The ''%%WaitVBL()%%'' function is a wrapper function that initializes an ''%%IOInfo%%'' structure using the ''%%TIMERCMD_DELAY%%'' command, and sends it to the timer device. ==== Getting Repeated Notifications ==== Similar to the microsecond unit, the vblank unit can automatically send you signals at specified intervals. You specify the interval time in terms of vblanks. Unlike with the microsecond unit, you do not specify the amount of time between signals using a TimeVal structure. Instead, set the''%% ioi_Offset%%'' field of the IOInfo structure to the vblank count between signals. ''%%%%'' IOInfo ioInfo; int32 signals; memset(&ioInfo,0,sizeof(ioInfo)); ioInfo.ioi_Command = TIMERCMD_METRONOME; ioInfo.ioi_Unit = TIMER_UNIT_VBLANK; ioInfo.ioi_CmdOptions = signals; ioInfo.ioi_Offset = vblanksBetweenSignals; You should send the I/O request to the timer device using ''%%SendIO()%%''. Once this is done, the timer device will send a signal to your task every time the specified number of vblanks occurs. To stop the timer device from sending you these signals, you must abort the I/O request using the''%% AbortIO()%%'' call. ===== High-Performance Timing ===== It is sometimes necessary to measure very short time intervals with very high accuracy. This is especially useful when trying to measure the performance of various pieces of code. Although using the timer device and the ''%%CMD_READ%%'' command gives fairly accurate readings, the overhead involved in doing device I/O is often enough to skew the results of small time intervals. Portfolio provides the ''%%SampleSystemTime()%%'' and ''%%SampleSystemTimeTV()%%'' functions that allow low-overhead sampling of the system clock. These functions are based on the same hardware clock as the microsecond unit of the timer device, and so refer to the same time base. ''%%SampleSystemTime()%%'' returns the current seconds counter of the system clock. The function also returns the current microseconds counter in the R1 CPU register of the ARM processor. This value is only available if you program in assembly language. To get both values in C, you must use the ''%%SampleSystemTimeTV()%%'' function, which puts the current values of the seconds and microseconds counters in the TimeVal structure that you supply. ===== Time Arithmetic ===== The following calls calculate or compare time values. * AddTimes(). Adds two time values, yielding the total time for both. This call is useful for time calculation. * CompareTimes(). Compares two time values. This call helps you determine what happens when. * SubTimes(). Subtracts one time value from another, yielding the time difference between the two. * TimeLaterThan(). Returns whether a time value comes before another time value. * TimeLaterThanOrEqual(''%%)%%''. Returns whether a time value comes before or at the same time as another time value. ===== Simplified Timer Device Interface ===== The following calls create I/O requests and wait fixed amounts of time. These are simple convenience calls that interface to the timer device for you. All of these routines use the microsecond timer unit. * CreateTimerIOReq(). Creates an I/O request for communication with the timer device. * DeleteTimerIOReq(). Frees any resources used in a previous call to ''%%CreateTimerIOReq()%%''. * * WaitUntil(). Puts the current context to sleep until the system clock reaches a given time.