PIT Help

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
QBasicer

PIT Help

Post by QBasicer »

First I must really tell you what's going on. I'm using a modified OSD9 "package" from http://www.execpc.com/~geezer/osd/code.

I'll give you a little code:

Code: Select all

   init_8259s();

   kprintf("[MSG] Installing keyboard interrupt handler...\n");
/* we don't save the old vector */
   v.eip = (unsigned)keyboard_irq;
   v.access_byte = 0x8E; /* present, ring 0, '386 interrupt gate */
   setvect(&v, 0x21);

   kprintf("[MSG] Setting Timer Frequency\n");
   timer_phase(100000);

   vector_t vtimer;
   v.eip = (unsigned)timer_irq;
   v.access_byte = 0x8E;
   setvect(&vtimer, 0x08);

   init_tasks();


   kprintf("[MSG] Enabling hardware interrupts...\n");
   enable();
I think the PIT is firing because text output is REALLY slow.

Next I'll show you    init_8259s();

Code: Select all

static void init_8259s(void)
{
   static const unsigned irq0_int = 0x20, irq8_int = 0x28;
/**/

/* Initialization Control Word #1 (ICW1) */
   outportb(0x20, 0x11);
   outportb(0xA0, 0x11);
/* ICW2:
route IRQs 0-7 to INTs 20h-27h */
   outportb(0x21, irq0_int);
/* route IRQs 8-15 to INTs 28h-2Fh */
   outportb(0xA1, irq8_int);
/* ICW3 */
   outportb(0x21, 0x04);
   outportb(0xA1, 0x02);
/* ICW4 */
   outportb(0x21, 0x01);
   outportb(0xA1, 0x01);
/* enable IRQ0 (timer) and IRQ1 (keyboard) */
   outportb(0x21, ~0x03);
   outportb(0xA1, ~0x00);
}
And timer_phase(int hz)

Code: Select all

void timer_phase(int hz)
{
    disable();
    int divisor = 1193180 / hz;       /* Calculate our divisor */
    outportb(0x43, 0x36);             /* Set our command byte 0x36 */
    outportb(0x40, divisor & 0xFF);   /* Set low byte of divisor */
    outportb(0x40, divisor >> 8);     /* Set high byte of divisor */
    enable();
}
And my exception handler:

Code: Select all

void fault(regs_t *regs)
{
   static const char * const msg[] =
   {
      "divide error", "debug exception", "NMI", "INT3",
      "INTO", "BOUND exception", "invalid opcode", "no coprocessor",
      "double fault", "coprocessor segment overrun",
         "bad TSS", "segment not present",
      "stack fault", "GPF", "page fault", "??",
      "coprocessor error", "alignment check", "??", "??",
      "??", "??", "??", "??",
      "??", "??", "??", "??",
      "??", "??", "??", "??",
      "IRQ0", "IRQ1", "IRQ2", "IRQ3",
      "IRQ4", "IRQ5", "IRQ6", "IRQ7",
      "IRQ8", "IRQ9", "IRQ10", "IRQ11",
      "IRQ12", "IRQ13", "IRQ14", "IRQ15",
      "syscall"
   };
/**/

   switch(regs->which_int)
   {
/* this handler installed at compile-time
Keyboard handler is installed at run-time (see below) */
   case 0x20:   /* timer IRQ 0 */
      ticks++;
      if (!(ticks % 310)){
         blink();
         kprintf("[SYS] %u\n", ticks/310);
      }
/* reset hardware interrupt at 8259 chip */
      outportb(0x20, 0x20);
      break;
   case 0x08:
      blink();
      outportb(0x20, 0x20);
      break;
   default:
      kprintf("Exception #%u", regs->which_int);
      if(regs->which_int <= sizeof(msg) / sizeof(msg[0]))
         kprintf(" (%s)", msg[regs->which_int]);
      kprintf("\n");
      dump_regs(regs);
      panic("Goodbye (system halted, use reset button to end)");
   }
}
I get a timer on 0x20, I figure though IRQ remapping in init_8259s(). I can't seem to edit it's fireing rate, however.

I tried setting it to fire twice a second, but that didn't seem to work.

I tried looking for int 0x8, like I read on google, but I don't get anything there.

I'm sorry if I've already asked something that's already been asked, I've been doing a lot of searching lately, including google, the forums, and the wiki.

Thanks!
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re:PIT Help

Post by Brendan »

Hi,
QBasicer wrote:I tried looking for int 0x8, like I read on google, but I don't get anything there.
That won't work because you've remapped the PIC chips. Instead you need to use interrupt 0x20 for IRQ 0, or "setvect(&vtimer, 0x20);".
QBasicer wrote:I tried setting it to fire twice a second, but that didn't seem to work.
For 2 Hz you're doing "1193180 / 2" which is 596590 (or 0x00091A6E). Then you're setting the low byte of the divisor to 0x6E and the high byte to 0x1A. This actually gives you a divisor of 0x1A6E (or 6766), which would be a frequency of roughly 176.35 Hz. Your 32 bit result is being truncated to 16 bits, which is all the hardware supports.

You're also using "mode 3". In this mode you can't use a divisor of 1. Further a divisor of 65536 can be used in by setting the hardware for a divisor of 0 (which gives 18.2 Hz - the lowest frequency the timer can possibly handle).

Anyway, try something like this:

For example:

Code: Select all

void timer_phase(int hz)
{
    int divisor;

    divisor = 1193180 / hz;              /* Calculate our divisor */
    if(divisor == 0) {
       kprintf("WARNING: PIT frequency too high, using 1193180 Hz.\n").
       divisor = 1;
    }
    if(divisor == 1) {
       disable();
       outportb(0x43, 0x34);             /* Set our command byte 0x34 (mode 2) */
       outportb(0x40, divisor & 0xFF);   /* Set low byte of divisor */
       outportb(0x40, divisor >> 8);     /* Set high byte of divisor */
       enable();
    } else {
       if(divisor >= 65536) {
          if(divisor > 65536) {
             kprintf("WARNING: PIT frequency too low, using 18.2 Hz.\n").
          }
          divisor = 0;
       }
       disable();
       outportb(0x43, 0x36);             /* Set our command byte 0x36 (mode 3) */
       outportb(0x40, divisor & 0xFF);   /* Set low byte of divisor */
       outportb(0x40, divisor >> 8);     /* Set high byte of divisor */
       enable();
    }
}
This code should correctly set a frequency from 18.2 Hz to 1193180 Hz, using mode 3 (or mode 2 if it has to).

You should also be aware that it "rounds up to nearest possible frequency" rather than using the frequency you ask for. For example, the PIT hardware will support 596590 Hz (divisor = 2) and 397727 Hz (divisor = 3), but there's nothing inbetween. If you ask for a frequency of 400000 Hz you'd end up with 596590 Hz instead (almost 50% faster than what you wanted).

Regardless of what you do you'll never get 2 Hz from the PIT chip. Instead you'd want to set it for a higher frequency and then count how many IRQs occur. For example, if you set the PIT to 200 Hz you could do something like:

Code: Select all

void PIT_IRQhandler(void) {
   PIT_IRQcounter++;
   if(PIT_IRQcounter >= 100) {
      kprintf("Woot! Another 500 mS has passed.\n");
      PIT_IRQcounter = 0;
   }
}
Of course for this you'd want to make sure your "kprintf" code is re-entrant, but the "kprintf" code would be run twice per second, even though the IRQ handler is run 200 times per second.


Cheers,

Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
User avatar
Pype.Clicker
Member
Member
Posts: 5964
Joined: Wed Oct 18, 2006 2:31 am
Location: In a galaxy, far, far away
Contact:

Re:PIT Help

Post by Pype.Clicker »

I tried setting it to fire twice a second, but that didn't seem to work.
oh, you're trying that on real hardware, right ?? because emulators like bochs typically fail to offer realtime simulation.
I tried looking for int 0x8, like I read on google, but I don't get anything there.
If you're looking for documents on how to control the timer, look out for "Programmable Interval Timer" (PIT): that's the name of the chip. If we're lucky enough, they should all be back in the hardware section of OSRC

<sigh> once again, that was all in the QuickLinkz</sigh>


I'm sorry if I've already asked something that's already been asked, I've been doing a lot of searching lately, including google, the forums, and the wiki.

Thanks!
Post Reply