Page 1 of 1
Implementing a timer to update a clock
Posted: Tue Mar 01, 2011 2:10 pm
by sema4
I am a noob at OS development, so bear with me.
I want to add a timer IRQ to my OS, and use it to update a clock every second. I understand how to get the time from CMOS because I have seen that in many other OSs. In my OS, I want to have the clock always displayed in the corner of the screen. Can anyone post any code on how to create this timer and use it to update the clock?
Thanks.
Re: Implementing a timer to update a clock
Posted: Tue Mar 01, 2011 2:22 pm
by Tosi
For a timer that automatically fires and IRQ, I would recommend the
PIT since you are new. There is also an
APIC local timer, but I don't even use it because I'm not using SMP. Once you set up your timer, you can get the time from CMOS, and every time the timer IRQ fires, update that time by the timer's period. Then, in an idle loop or a separate program, display the time in the corner of the screen. This will require at least a basic
text driver, as well as some method for converting numeric values to strings and displaying them on the screen.
Re: Implementing a timer to update a clock
Posted: Tue Mar 01, 2011 9:43 pm
by DavidCooper
Why not just use the clock's own IRQ? Write an IRQ8 routine and it'll fire once a second, thus allowing you to use it to trigger a redisplay of the time whenever the time actually changes rather than having a timer interrupt many times a second just to see if the time has changed.
My IRQ8 routine puts EAX and EDI on the stack, reads one or more bytes of CMOS RAM, copying them into an array using EDI to hold the array address, switches off the interrupt by sending 32 to port 32 and reading port 160 (these are all decimal values, so you may wish to convert them to hex), restores registers from stack and then ends with an iret. For some reason I always send 12 to port 112 and read port 113 before sending 32 to port 32, so that may have some additional role in switching off the interrupt - I can't remember as I wrote it years ago and I don't have my notes at hand. Don't just slavishly copy this without understanding it - try to find out how it works and what the ports that it uses actually are. The letters PIC should be worth investigating.
I'm going to have to add a bit of code to my interrupt routine to trigger a redisplay of the time because currently any program wanting to display the time simply redisplays periodically and reads the array written by the IRQ8 routine - the correct way to do things would be for the IRQ8 routine to send a message to wake the program to tell it to redisplay the time.
To handle the packed BCDs you need to split the higher four bits off and put them in the lower four bits of another byte, clear the higher four bits of each byte, then you could use AAD 10 to turn them into a binary value or add 48 to each to convert them into two ASKII digits for printing to the screen (again these are decimal values because I do all my programming using decimals).
Re: Implementing a timer to update a clock
Posted: Wed Mar 02, 2011 9:03 am
by rdos
This is a complex issue, if one wants a more general-purpose functionality for hi-res time & timers.
There are a varity of devices that can provide elapsed time (timestamp counter, CMOS, counting tics on PIT) and at least two ways to create timers (PIT or APIC timer).
I first check if the timestamp counter and APIC timer are present. If they are, I use the APIC timer to create timers and timestamp counter to keep elapsed time (and real time). If those are not available, I use PIT, channel 0, to create timers, and PIT, channel 2, to keep elapsed time (and real time). In both these scenarios, I use the CMOS IRQ to synchronize elapsed time to real time. This way I have real time with high precision (us), and I can create timers with 1us resolution. I can also synchronize the clock with a NTP-server. Real time is kept by adding a constant to the system time. System time will always increase between reads, even if real time has been adjusted. System time is set from the CMOS at boot-time (diff to real time is set to 0).
I implement timers by having a sorted list of expire-times. I then program PIT or APIC timer with the count that corresponds to the head expire time. When the IRQ fires, I run a callback associated with the timer, update the timer list, and select the next expire time. This way there can be many timers with arbitary timeouts. The scheduler uses a timer to preempt the currently running task, and because the preempt timeout is always less than the range of the PIT (1/28 s), the head in the timer-list can always be programmed directly in the PIT (or APIC timer).
I implement real time / system time as part of the timer functionality. Whenever real-time / system-time is read (this is done as part of the timer implementation), either the timestamp counter or freerunning PIT channel 2 will be read, and the number of tics since the last readout will be calculated. This difference will then be multiplied by a scale factor to yield the difference in system time, and then this difference is added to the current system time. The scale-factor is adjusted by the periodic CMOS IRQ to keep the system time running smothly even if there is variation in the frequency of the PIT or timestamp counter.