Programmable Interval Timer: Difference between revisions

Jump to navigation Jump to search
[unchecked revision][unchecked revision]
Content deleted Content added
Brendan (talk | contribs)
m Simplified the PIT Channel 0 Example Code
Added information on using the timer IRQ.
Line 414: Line 414:


Of course it's easier to configure the PIT to a fixed value, but where's the fun in that? :-)
Of course it's easier to configure the PIT to a fixed value, but where's the fun in that? :-)

==Uses for the Timer IRQ==
===Using the IRQ to Implement <tt>sleep</tt>===
The PIT's generating a hardware interrupt every ''n'' milliseconds allows you to create a simple timer. Start with a global variable x that contains the delay:
SEGMENT .DATA
CountDown DD 0
Next, every time the timer interrupt is called, decrement this variable until 0 is stored.
SEGMENT .TEXT
[GLOBAL TimerIRQ]
TimerIRQ:
PUSH EAX
MOV EAX, CountDown
OR EAX, EAX ; quick way to compare to 0
JZ TimerDone
DEC CountDown
TimerDone:
POP EAX
IRETD
Finally, create a function <tt>Sleep</tt> that waits the interval, in milliseconds. I assume Pascal calling convention - the called function cleans the stack.
[GLOBAL _Sleep]
_Sleep:
PUSH EBP
MOV EBP, ESP
PUSH EAX
MOV EAX, [EBP + 8] ; EAX has value of sole argument
MOV CountDown, EAX
SleepLoop:
CLI ; can't be interrupted for test
MOV EAX, CountDown
OR EAX, EAX
JZ SleepDone
STI
NOP ; NOP a few times so the interrupt can get handled
NOP
NOP
NOP
NOP
NOP
JMP SleepLoop
SleepDone:
POP EAX
POP EBP
RETN 8

In a multitasking system, consider using a linked list or array of these CountDown variables. If your multitasking system supports interprocess communication, you can also store the semaphore/exchange where two processes can talk to, have the interrupt send a message to the waiting process when the timer is done, and have the waiting process block all execution until that message comes:
#define COUNTDOWN_DONE_MSG 1
struct TimerBlock {
EXCHANGE e;
u32int CountDown;
} timerblocks[20];
void TimerIRQ(void) /* called from Assembly */
{
u8int i;
for (i = 0; i < 20; i++)
if (timerblocks[i].CountDown > 0) {
timerblocks[i].CountDown--;
if (timerblocks[i].CountDown == 0)
SendMessage(timerblocks[i].e, COUNTDOWN_DONE_MESSAGE);
}
}
void Sleep(u32int delay)
{
TimerBlock *t;
if ((t = findTimerBlock()) == nil)
return;
asm("cli");
t->CountDown = delay;
WaitForMessageFrom(t->e = getCrntExch());
}

In your documentation, note the interval of the timer. For example, if the timer interval is 10 milliseconds per tick, tell the programmer to issue
Sleep(100);
to sleep for a single second.

===Using the IRQ for Preemptive Multitasking===
The timer IRQ can also be used to perform preemptive multitasking. To give the currently running task some time to run, set a threshold, for example of 3 ticks. Use a global variable like the one before but go up from 0, and when that variable hits 3, switch tasks. How you do so is up to you.


[[Category:Common Devices]]
[[Category:Common Devices]]