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 The Vertical Blank Unit. Both units respond to the same commands.
Using either of the timer units, you can do four basic operations:
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.
OpenNamedDevice()
function.
CreateIOReq()
function.
DoIO()
or SendIO().
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;
The
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);
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 High-Performance Timing.
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:
The
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);
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.
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.
TIMERCMD_METRONOME
commands arranges to have a signal sent to your task for an undetermined length of time at a fixed rate.
The
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);
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:
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
typedef struct VBlankTimeVal
{
uint32 vbltv_VBlankHi32; * upper 32 bits of vblank counter */
uint32 vbltv_VBlankLo32; /* lower 32 bits of vblank counter */
} VBlankTimeVal;
vbltv_VBlankHi32
field, while the lower-32 bits are stored in the vbltv_VBlankLo32
field.
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.
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;
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.
IOInfo ioInfo;
memset(&ioInfo,0,sizeof(ioInfo));
ioInfo.ioi_Command = TIMERCMD_DELAY;
ioInfo.ioi_Unit = TIMER_UNIT_VBLANK;
ioInfo.ioi_Offset = numberOfVBlanksToWait;
The WaitVBL()
function is a wrapper function that initializes an IOInfo
structure using the TIMERCMD_DELAY
command, and sends it to the timer device.
ioi_Offset
field of the IOInfo structure to the vblank count between signals.
You should send the I/O request to the timer device using
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;
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.
)
. Returns whether a time value comes before or at the same time as another time value.
CreateTimerIOReq()
.