Hey, I tried putting my ISR handlers in the same file as my IDT table and now it seems to work - the file assembles and lidt instruction seems to register the IDT table. I think I understand the problem I had a little more. The next problem now is that I get exception 9. I tried finding out what causes the exception and I think the interrupt 9 is fired when my hang loop starts. I don't know why in the world anything in that loop would cause the exception.
My console output.
debug
received interrupt: 9
error code: 0
debug
I tried removing the hlt instruction and having just a simple loop without nothing in it and the interrupt still fires, so it is not the hlt instruction that is causing it. I suppose the loop itself couldn't cause the interrupt 9, could it?
I also found this
http://f.osdev.org/viewtopic.php?f=1&t=11002&start=0. So I thought that maybe something is wrong with my keyboard handler, but I do not press any buttons before the exception 9, so the interrupt 33 can't be fired, so it can't be the keyboard handlers fault, can it?
Also, the strange thing is not only that the interrupt 9 fires, but that my PIT handler does not get executed and the same goes for my keyboard handler - the keyboard does not work. Or maybe my PIT handler does get executed and that is what causes the exception? But why the exception? I can't find anything that would cause it. Besides, the handler code is the same as when I had my IDT setup in C and it worked fine then. And could the exception 9 disable interrupts, so that neither the PIT nor the keyboard handlers could be executed? I don't think so (Actually, after I wrote almost all of this, I inserted an int instruction after the hlt one in the hang loop and the interrupt does occur after the exception 9 (This raises another question for me - how does the hlt instruction work? Why does the int fire an interrupt if the hlt is supposed to halt the computer and, as I understand it, do nothing?)). I double checked and the interrupt handler does re-enable the interrupts. (Also, on a side note, I was wondering - doesn't the processor itself disable interrupts when an interrupt is fired and re-enable them when you return from one using iret instruction? I could swear I read that it does somewhere. (Actually, again, I tried out the handler without sti at the end and the processor seems to automatically enable interrupts when the interrupt handler returns. So is JamesM's tutorial wrong on this, as I partially based my kernel on it? I heard the are bugs.)) Here's my assembly (With all the cli and sti not yet removed) as well as C parts of the interrupt handler.
Code: Select all
commonisr:
pusha ; Pushes edi, esi, ebp, esp, ebx, edx, ecx, eax.
mov ax, ds ; Lower 16-bits of eax = ds.
push eax ; Save the data segment descriptor.
mov ax, 0x10 ; Load the kernel data segment descriptor.
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
call handlisr
pop eax ; Reload the original data segment descriptor.
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
popa ; Pops edi, esi, ebp, ...
add esp, 8 ; Cleans up the pushed error code and pushed ISR number.
sti
iret
%macro ISRNOERRCODE 1 ; Define a macro, taking one parameter.
global isr%1 ; %1 accesses the first parameter.
isr%1:
cli
push byte 0
push byte %1
jmp commonisr
%endmacro
%macro ISRERRCODE 1
global isr%1
isr%1:
cli
push byte %1
jmp commonisr
%endmacro
ISRNOERRCODE 0
ISRNOERRCODE 1
...
ISRNOERRCODE 47
Code: Select all
void handlisr(registers_t regs)
{
int scan;
if(regs.intrno < 32)
{
prntstr("received interrupt: " );
prntstr(int2str(regs.intrno));
prntc('\n');
prntstr("error code: ");
prntstr(int2str(regs.errcode));
prntc('\n');
if(intrhandl[regs.intrno] != 0)
}else{
if(regs.intrno == 32)
{
prntstr("tick: ");
prntstr(int2str(tick));
prntc('\n');
tick++;
}
if(regs.intrno == 33)
{
scan = ib(0x60);
if(scan & 128)
{
// Released.
// Codes -86 and -74 stand for reeased left and right shift keys respectively.
if(scan == -86 || scan == -74)
{
shift = shift-1;
}
}else{
// Pressed.
// Codes 42 and 54 stand for pressed left and right shift key respectively.
if(scan == 42 || scan == 54)
{
shift = shift+1;
}else{
if(shift == 0)
{
prntc(lowercase[keyno]);
}else{
prntc(uppercase[keyno]);
}
}
}
}
// Send an EOI (end of interrupt) signal to the PICs.
// If this interrupt involved the slave.
if(regs.intrno >= 40)
{
ob(0xA0, 0x20); // Send reset signal to slave.
}
ob(0x20, 0x20); // Send reset signal to master.
}
}
This might sound silly, but does ALL the kernel have to be in a single assembly source file and does it REALLY have to be a flat binary file to work? Because the object files link fine and the exception handler works without a problem too.
Also I had a look again at the IDT layout. Again, mine is like this.
Code: Select all
%macro IDTENTRY 1
dw (SECTIONBASE + isr%1 - $$) & 0xffff ; Base low.
dw 0x08 ; Selector.
db 00000000b ; Always zero.
db 10001110b ; Flags.
dw (SECTIONBASE + isr%1 - $$) >> 16 ; Base high.
%endmacro
idt:
IDTENTRY 1
IDTENTRY 2
...
IDTENTRY 47
And it seems fine. I know I'm practically asking you to check it, but I already checked it several times and if there is something wrong, I keep missing it. Am I possibly setting any values so that my ISR handlers would be called incorrectly?
And one last thing. I did not want to do it the equation way, that Combuster suggested, as it seems inelegant IMO and I think it would make the kernel bigger and yes, I know that it's just a couple of bytes, besides, it is boot code, which is executed only once, thus not worth the effort trimming in most peoples opinion, but I'm a little OCD about it and I think it all adds up and if you don't save bytes from the beginning, you will have a noticeably bigger kernel. This goes with no disrespect to you, Combuster, as what you said helped me and I have read these forums for quite a while and think you are one of the more knowledgeable people around here.
Hope there's not too much questions and code. : )