Page 1 of 1

Problem with PICs not firing interrupts

Posted: Tue Dec 19, 2006 8:09 pm
by agrif
Hello. I've been programming for a while as a hobby, and recently I thought it would be fun to try and write a very simple PC kernel to try and learn what actually goes on when the computer boots, and how thing hardware interfaces with the OS. I started to follow this tutorial on osdever.com, but I started having some trouble with IRQs. None of them ever fired, not for the keyboard or even the timer. The Interrupt handlers are installed alright (IRQ0 fires when I run "int 0x20"), but the PIC either doesn't have the right IDT entry number, or something else is terribly wrong. I'm hoping that I've at least got this right: It has to be a problem with the PIC not firing correctly.

The only code I have that deals with the PICs is here:

Code: Select all

OUTPORTB(0x20, 0x11); 
OUTPORTB(0xA0, 0x11);
OUTPORTB(0x21, 0x20);
OUTPORTB(0xA1, 0x28);
OUTPORTB(0x21, 0x04);
OUTPORTB(0xA1, 0x02);

OUTPORTB(0x21, 0x01);
OUTPORTB(0xA1, 0x01);

OUTPORTB(0x21, 0x0);
OUTPORTB(0xA1, 0x0);
where OUTPORTB is a preprocessor macro:

Code: Select all

#define OUTPORTB(_port, _data) __asm__ __volatile__ ("outb %1, %0" : : "dN" ((unsigned short)_port), "a" ((unsigned char)_data))
Is my outportb wrong? I've been staring at this for days, looking up all the info on the PIC, and all of them point to this code being right. I swear it's not anything else, or else the interrupt instruction wouldn't work, right?

I've tested this code on bochs, qemu, and on my desktop and laptop. None of them show any hint of working. I'm compiling with gcc 3.4.4 on Gentoo, and the kernel is always being booted by GRUB. That's all the relevant info I can think of right now...

On a side note, I'm trying to make sure I understand the code above. The first two lines tell the PICs that a reconfiguration is coming. The next two tell IRQ0-IRQ7 to map to interrupts 0x20-0x27, and IRQ8-IRQ15 to 0x28-0x2F. The next to tell the master PIC to communicate to the slave PIC on IRQ2 (bit 2 set), and the slave PIC to the master PIC on IRQ9 (bit 1 set). The next two tell the PICs how to communicate (or something, don't quite get this part), and the last two set the IRQ masks off.

Right?

Thanks for any help you can give me. I'm really interested in this, but I'm stumped and frustrated right now.

agrif

Posted: Tue Dec 19, 2006 10:48 pm
by m
Hi.

I've read your code but haven't tested that.

First you initialize the PIC by writing ICW1,ICW2 and ICW3 to both the master and the slave PIC,and the order seems pretty correct.Next you enable all the IRQs by writing 0 to the ports.It seems nothing wrong.

If it doesn't work,the problem may be that you don't have a I/O delay after each OUTPORTB.It'll probably take the PICs some time to process the command and respond.If this is the reason why it doesn't work,you can simply add a I/O-delay procedure after each OUTPORTB.

For example,in assembly,you can use several NOP instructions as the delay procedure

Code: Select all

delay:
nop
nop
...      ;You'd better have a try to find out how many will work.
or

Code: Select all

delay:
loop $    ;Test it to decide the value of (E)CX to be set.
If the delay is still too short,use a programmable timer to generate a interrupt for you.

I hope this will help. :D

Posted: Wed Dec 20, 2006 1:34 am
by jhawthorn
m wrote: If it doesn't work,the problem may be that you don't have a I/O delay after each OUTPORTB.It'll probably take the PICs some time to process the command and respond.If this is the reason why it doesn't work,you can simply add a I/O-delay procedure after each OUTPORTB.
If it isn't working on qemu or bochs this shouldn't be his problem.
m wrote:If the delay is still too short, use a programmable timer to generate a interrupt for you.
This code is to enable interrupts so the interrupt shouldn't fire. I'm not all that comfortable with using a PIT for an io_wait function anyways. The general consensus seems to be using the approach from linux (using port 0x80).

Addressing agrif's problem, your code has the same order, ports, and values as an old kernel I have lying about so that does not look to be the culprit. This may be a silly question, but did you remember to STI?

Posted: Wed Dec 20, 2006 4:36 am
by m
m wrote:
If the delay is still too short, use a programmable timer to generate a interrupt for you.
This code is to enable interrupts so the interrupt shouldn't fire.
Ah,yes,I've made a stupid mistake. :shock:
Anyway I'm using the 1st version in my code. :wink:

Posted: Wed Dec 20, 2006 12:25 pm
by Mikae
Another silly question: did you send EOI at the end of interrupt handler?

Posted: Wed Dec 20, 2006 2:37 pm
by agrif
I knew I forgot something. I've gotta learn to never write a post before I go to bed.

Mikae: yeah, I send a 0x20 to the command port for the master PIC, and also the slave if it needs it. That is the right way, right?

m: I also forgot to mention I tried adding a delay anyway. I saw it on some site or another and thought it was a good idea. It didn't work. Good thing I know that it'll be a good idea later, though.

jhawthorn: It's good to see I didn't do something stupid. I'm new to this, and I have no idea what STI stands for. I would search for it, but all searches for STI return buisnesses, and all searches for PIC return generic do-it-yourself circuits for a programmable chip. :P

Thanks for all the responses! It's always wonderful to know theres a place I can ask questions in when I hit a dead end.

Another bit of info that may be of use, now that I think about it, is that the IRQ and ISR handler stubs in ASM are assembled by GNU ar. I noticed that most of you use NASM, and maybe this makes a difference. Although, it shouldn't if my interrupts are working otherwise.

Thanks again,

agrif

Posted: Wed Dec 20, 2006 3:01 pm
by Mikae
'sti' is cpu instruction, which sets IF flag in EFLAGS. CPU responces on interrupts only when IF is set. 'cli' clears IF, and 'sti' sets it. You have to disable interrupts before programming PIC and to enable them after.

Also, check if your handlers are installed correctly. The best way to do it, IMHO, is to comment out code, which programms PIC, leaving only code of handlers and code which installs handlers. Then look, if your code was executed.

Posted: Wed Dec 20, 2006 3:16 pm
by agrif
Mikae: oh, yeah... I was thinking that STI stood for some obscure flag that you have to send somewhere... :oops: I remembered the STI instruction. Also, I'm sure that the handlers are installed, and in the right place, too. If I use int $0x20 (where my IRQ0 should be), it calls the function installed there. I even interrupted twice in a row to make sure the interrupts are turned back on after the first one.

thanks for your help

---

edited on Dec 20, 7:26 EST

As Mikae pointed out, I had forgotten the STI. I always assumed that STI and CLI were only used during interrupt handlers to make sure that nothing else fired while you were handling it... but as wikipedia pointed out, I need STI to enable IRQ fires...

I knew it was going to be something completly different than what I thought... thanks for all the help, everyone!