IRQs - Why and What
IRQs - Why and What
I had some questions about IRQs and I would really appreciate it if somebody could answer them for me.
1. Do we have to handle IRQs using Interrupt Service Routines just as we handle traps?
2. We have IRQ #0 and Division by Zero Trap which uses the #0 index in the IDT. Do we have to tell the CPU which one to handle?
3. Are IRQs enabled and disabled with STI/CLI respectively?
4. Are IRQs sent to the CPU without the initialization of Control Words of the 8259 Chip?
Thanks in advance.
1. Do we have to handle IRQs using Interrupt Service Routines just as we handle traps?
2. We have IRQ #0 and Division by Zero Trap which uses the #0 index in the IDT. Do we have to tell the CPU which one to handle?
3. Are IRQs enabled and disabled with STI/CLI respectively?
4. Are IRQs sent to the CPU without the initialization of Control Words of the 8259 Chip?
Thanks in advance.
Re: IRQs - Why and What
When an IRQ (from a peripheral device) occurs, it *triggers* an interrupt in the cpu, and thus an interrupt service routine is called. It basically is the same, the main difference is that you'll have to send at least an EOI to the interrupt controller (in addition to IRET).XCHG wrote:1. Do we have to handle IRQs using Interrupt Service Routines just as we handle traps?
This is one thing that the programmable interrupt controller is for. It can map IRQs to certain interrupt ranges. Usually, you'll map IRQs into a higher range (like, starting at 0x20). This would mean that IRQ #0 now equals interrupt 0x20 and so on, thus you can distinguish them better.XCHG wrote:2. We have IRQ #0 and Division by Zero Trap which uses the #0 index in the IDT. Do we have to tell the CPU which one to handle?
When you disable the interrupt flag (with CLI), you won't receive any interrupts anymore, so you won't receive any IRQs either, but they'll still arrive at the interrupt controller. So you got 2 seperate stages here: The interrupt controller and the cpu itself. STI/CLI only controls the cpu receiving interrupts.XCHG wrote:3. Are IRQs enabled and disabled with STI/CLI respectively?
I'm not sure, but I guess the BIOS already initializes the 8259 with a basic set of functionality. It is recommended however that you at least remap the IRQ ranges (like mentioned above) and disable/enable IRQs as you need them (there is no need to enable a floppy controller's IRQ line, f.ex., when you can't handle the device yet).XCHG wrote:4. Are IRQs sent to the CPU without the initialization of Control Words of the 8259 Chip?
I hope I could help you!
cheers
Joe
Okay, that really helped. Thank you so much. Although I have one more question about IRQs if anybody is willing to help.
I have set up an IDT with 50 slots. I then initialize the 8259A and map IRQs to 0x20 and 0x28 in my IDT. I then build the first 32 Trap Handlers in my IDT for CPU generated exceptions/traps and load the IDT and set the interrupt flag in the Flags Register. What happens after that is strange because the kernel does *not* crash but whenever a key is pressed on the keyboard, my Int6Handler (Invalid Opcode Exception) is called. Can anybody help me with this or tell me what I might be doing wrong?
I have set up an IDT with 50 slots. I then initialize the 8259A and map IRQs to 0x20 and 0x28 in my IDT. I then build the first 32 Trap Handlers in my IDT for CPU generated exceptions/traps and load the IDT and set the interrupt flag in the Flags Register. What happens after that is strange because the kernel does *not* crash but whenever a key is pressed on the keyboard, my Int6Handler (Invalid Opcode Exception) is called. Can anybody help me with this or tell me what I might be doing wrong?
When a key on the keyboard is pressed it would send an IRQ1 when the CPU calls the interrupt handler for IRQ1 it could point to an invalid opcode. Try setting up the rest of the interrupt handlers, the ones for the IRQs.XCHG wrote:Okay, that really helped. Thank you so much. Although I have one more question about IRQs if anybody is willing to help.
I have set up an IDT with 50 slots. I then initialize the 8259A and map IRQs to 0x20 and 0x28 in my IDT. I then build the first 32 Trap Handlers in my IDT for CPU generated exceptions/traps and load the IDT and set the interrupt flag in the Flags Register. What happens after that is strange because the kernel does *not* crash but whenever a key is pressed on the keyboard, my Int6Handler (Invalid Opcode Exception) is called. Can anybody help me with this or tell me what I might be doing wrong?
Thank you for your reply. I have set the procedures for all CPU generated traps/interrupts/faults up to 0x20 and then I created two procedures to handle IRQs that are sent from the master PIC and another for the slave 8259A. The code for my kernel is given below:
I then tried aligning all the interrupt handler procedures' codes on a DWORD boundary and it didn't work either. There must be something wrong with the definition of the procedures I reckon because why would the CPU report "Invalid Opcode Exception" when a key is pressed on the keyword? Or maybe there is something wrong with the way I am initializing the PICs.
I'd really appreciate it if somebody could help me.
Code: Select all
[BITS 32]
[ORG 0x00100000]
VIDEOSEGMENT EQU 0x000B8000
VIDEOBYTECOUNT EQU (80*25) << 1
KERNELENTRY EQU 0x00100000
KERNEL_SIZE_IN_KB EQU 0x04
IDT_ENTRY_COUNT EQU 0x30
; --------------------------------------------------
%IDEFINE OFFSET
%IDEFINE PTR
; --------------------------------------------------
%MACRO SETIDTENTRY 2 ; (int IDTNumber , void IDTProcedure)
MOV EBX , OFFSET IDT + (%1 * 0x08)
MOV EAX , %2
MOV WORD PTR [EBX] , AX
MOV WORD PTR [EBX + 0x02] , 0x0008
MOV WORD PTR [EBX + 0x04] , 0x8E00
SHR EAX , 0x10
MOV WORD PTR [EBX + 0x06] , AX
%ENDMACRO
; --------------------------------------------------
PIC1 EQU 0x20
PIC2 EQU 0xA0
PIC1_COMMAND EQU PIC1
PIC1_DATA EQU (PIC1+1)
PIC2_COMMAND EQU PIC2
PIC2_DATA EQU (PIC2+1)
PIC_EOI EQU 0x20
ICW1_ICW4 EQU 0x01
ICW1_INIT EQU 0x10
ICW4_8086 EQU 0x01
; --------------------------------------------------
[SECTION .text]
START:
CLI
CALL __ClearScreen
; Fill all the IDT slots with zero
MOV ECX , IDT_ENTRY_COUNT
MOV EBX , OFFSET IDT
XOR EAX , EAX
@@__FillIDTWithZero:
MOV DWORD PTR [EBX] , EAX
MOV DWORD PTR [EBX + 0x04] , EAX
ADD EBX , 0x08
DEC ECX
JNZ @@__FillIDTWithZero
; Initialize the 8259A PIC
IN AL , PIC1_DATA
MOV DL , AL
IN AL , PIC2_DATA
MOV DH , AL
MOV AL , ICW1_INIT | ICW1_ICW4
OUT PIC1_COMMAND , AL
OUT PIC2_COMMAND , AL
MOV AL , 0x20
OUT PIC1_DATA , AL
MOV AL , 0x28
OUT PIC2_DATA , AL
MOV AL , 0x04
OUT PIC1_DATA , AL
MOV AL , 0x02
OUT PIC2_DATA , AL
MOV AL , ICW4_8086
OUT PIC1_DATA , AL
OUT PIC2_DATA , AL
MOV AL , DL
OUT PIC1_DATA , AL
MOV AL , DH
OUT PIC2_DATA , AL
; Set IDT entries for the first 32 slots
SETIDTENTRY 0x00 , __Trap0Handler
SETIDTENTRY 0x01 , __Trap1Handler
SETIDTENTRY 0x02 , __Trap2Handler
SETIDTENTRY 0x03 , __Trap3Handler
SETIDTENTRY 0x04 , __Trap4Handler
SETIDTENTRY 0x05 , __Trap5Handler
SETIDTENTRY 0x06 , __Trap6Handler
SETIDTENTRY 0x07 , __Trap7Handler
SETIDTENTRY 0x08 , __Trap8Handler
SETIDTENTRY 0x09 , __Trap9Handler
SETIDTENTRY 0x0A , __Trap10Handler
SETIDTENTRY 0x0B , __Trap11Handler
SETIDTENTRY 0x0C , __Trap12Handler
SETIDTENTRY 0x0D , __Trap13Handler
SETIDTENTRY 0x0E , __Trap14Handler
SETIDTENTRY 0x0F , __Trap15Handler
SETIDTENTRY 0x10 , __Trap16Handler
SETIDTENTRY 0x11 , __Trap17Handler
SETIDTENTRY 0x12 , __Trap18Handler
SETIDTENTRY 0x13 , __Trap19To31Handler
SETIDTENTRY 0x14 , __Trap19To31Handler
SETIDTENTRY 0x15 , __Trap19To31Handler
SETIDTENTRY 0x16 , __Trap19To31Handler
SETIDTENTRY 0x17 , __Trap19To31Handler
SETIDTENTRY 0x18 , __Trap19To31Handler
SETIDTENTRY 0x19 , __Trap19To31Handler
SETIDTENTRY 0x1A , __Trap19To31Handler
SETIDTENTRY 0x1B , __Trap19To31Handler
SETIDTENTRY 0x1C , __Trap19To31Handler
SETIDTENTRY 0x1D , __Trap19To31Handler
SETIDTENTRY 0x1E , __Trap19To31Handler
SETIDTENTRY 0x1F , __Trap19To31Handler
; Fill IRQ gates in the IDT for the first 8 IRQs
MOV EBX , OFFSET IDT + (0x20 * 0x08)
MOV ECX , 0x08
MOV EAX , OFFSET __MasterPICHandler
@@__FillIRQSlots1to8:
MOV WORD PTR [EBX] , AX
MOV WORD PTR [EBX + 0x02] , 0x0008
MOV WORD PTR [EBX + 0x04] , 0x8F00
ROR EAX , 0x10
MOV WORD PTR [EBX + 0x06] , AX
ADD EBX , 0x08
DEC ECX
JNZ @@__FillIRQSlots1to8
; Fill IRQ gates in the IDT for the last 8 IRQs
MOV ECX , 0x08
MOV EAX , OFFSET __SlavePICHandler
@@__FillIRQSlots9to16:
MOV WORD PTR [EBX] , AX
MOV WORD PTR [EBX + 0x02] , 0x0008
MOV WORD PTR [EBX + 0x04] , 0x8F00
ROR EAX , 0x10
MOV WORD PTR [EBX + 0x06] , AX
ADD EBX , 0x08
DEC ECX
JNZ @@__FillIRQSlots9to16
LIDT [IDTR]
STI
HLT
%INCLUDE "Procs.asm"
%INCLUDE "VideoT.asm"
; --------------------------------------------------
__MasterPICHandler:
MOV AL , PIC_EOI
OUT PIC1_COMMAND , AL
IRET
__SlavePICHandler:
MOV AL , PIC_EOI
OUT PIC2_COMMAND , AL
OUT PIC1_COMMAND , AL
IRET
; --------------------------------------------------
Trap0Msg DB 'Division by zero', 0x00
Trap1Msg DB 'Debug Exception', 0x00
Trap2Msg DB 'Non-maskable Interrupt Exception', 0x00
Trap3Msg DB 'Breakpoint Exception', 0x00
Trap4Msg DB 'Into Detected Overflow Exception', 0x00
Trap5Msg DB 'Out of Bounds Exception', 0x00
Trap6Msg DB 'Invalid Opcode Exception', 0x00
Trap7Msg DB 'No Coprocessor Exception', 0x00
Trap8Msg DB 'Double Fault Exception', 0x00
Trap9Msg DB 'Coprocessor Segment Overrun Exception', 0x00
Trap10Msg DB 'Bad TSS Exception', 0x00
Trap11Msg DB 'Segment Not Present Exception', 0x00
Trap12Msg DB 'Stack Fault Exception', 0x00
Trap13Msg DB 'General Protection Fault', 0x00
Trap14Msg DB 'Page Fault Exception', 0x00
Trap15Msg DB 'Unknown Interrupt Exception', 0x00
Trap16Msg DB 'Coprocessor Fault Exception', 0x00
Trap17Msg DB 'Alignment Check Exception', 0x00
Trap18Msg DB 'Machine Check Exception', 0x00
Trap19Msg DB 'Reserved Exception', 0x00
; --------------------------------------------------
__Trap0Handler:
___WriteStr Trap0Msg
JMP $
; --------------------
__Trap1Handler:
___WriteStr Trap1Msg
JMP $
; --------------------
__Trap2Handler:
___WriteStr Trap2Msg
JMP $
; --------------------
__Trap3Handler:
___WriteStr Trap3Msg
JMP $
; --------------------
__Trap4Handler:
___WriteStr Trap4Msg
JMP $
; --------------------
__Trap5Handler:
___WriteStr Trap5Msg
JMP $
; --------------------
__Trap6Handler:
___WriteStr Trap6Msg
JMP $
; --------------------
__Trap7Handler:
___WriteStr Trap7Msg
JMP $
; --------------------
__Trap8Handler:
___WriteStr Trap8Msg
JMP $
; --------------------
__Trap9Handler:
___WriteStr Trap9Msg
JMP $
; --------------------
__Trap10Handler:
___WriteStr Trap10Msg
JMP $
; --------------------
__Trap11Handler:
___WriteStr Trap11Msg
JMP $
; --------------------
__Trap12Handler:
___WriteStr Trap12Msg
JMP $
; --------------------
__Trap13Handler:
___WriteStr Trap13Msg
JMP $
; --------------------
__Trap14Handler:
___WriteStr Trap14Msg
JMP $
; --------------------
__Trap15Handler:
___WriteStr Trap15Msg
JMP $
; --------------------
__Trap16Handler:
___WriteStr Trap16Msg
JMP $
; --------------------
__Trap17Handler:
___WriteStr Trap17Msg
JMP $
; --------------------
__Trap18Handler:
___WriteStr Trap18Msg
JMP $
; --------------------
__Trap19To31Handler:
___WriteStr Trap19Msg
JMP $
; --------------------------------------------------
ALIGN 8, NOP
[SECTION .bss]
IDT:
RESD (IDT_ENTRY_COUNT << 1)
IDT_END:
;------------------------------
[SECTION .data]
IDTR:
DW (IDT_END - IDT) - 1
DD IDT
String1 DB 'Kernel', 0x00
String2 DB 'A key is pressed', 0
VideoCursor DD 0x00000000
TIMES (KERNEL_SIZE_IN_KB << 10)-($-$$) DB 0x00
I'd really appreciate it if somebody could help me.
Okay I made some changes to the code and now the IRQs are disabled by default:
I then wrote this procedure that enables a specific IRQ, given its vector number:
I then attempted to enable IRQ 0x01 and then the same problem happened again. As soon as I pressed a key on my keyboard, Invalid Opcode Exception was fired!!! [umm, makes a sad face]
Code: Select all
%MACRO OUTP 2
; %1 = Port Number
; %2 = Value
MOV AL , %2
OUT %1 , AL
%ENDMACRO
; --------------------------------------------------
OUTP PIC1 ,0x11
OUTP PIC2, 0x11
OUTP PIC1_DATA, 0x20
OUTP PIC2_DATA, 0x28
OUTP PIC1_DATA, 0x04
OUTP PIC2_DATA, 0x02
OUTP PIC1_DATA, 0x01
OUTP PIC2_DATA, 0x01
OUTP PIC1_DATA, 0xFF
OUTP PIC2_DATA, 0xFF
Code: Select all
; --------------------------------------------------
__EnableIRQ: ;AL = IRQ Number
MOV CL , AL
MOV AX , 0xFFFF
MOV DX , 0x0001
SHL DX , CL
NOT DX
AND AX , DX
OUTP PIC1_DATA , AL
MOV AL , AH
OUTP PIC2_DATA , AL
RET
I have defined the IDT in this way:
while the IDT_ENTRY_COUNT constant is set to 0x30 at the 7th step of the kernel's code. The IDT is creating (IDT_ENTRY_COUNT * 2) DWORDs which would make it (48 * 2) * 4 bytes = 384 bytes or enough room for 48 interrupt routines. Correct me if I am wrong because I have been trying to make this work for over a week now and no dice yet. Thank you for your reply.
Code: Select all
; --------------------------------------------------
ALIGN 8, NOP
[SECTION .bss]
IDT:
RESD (IDT_ENTRY_COUNT << 1)
IDT_END:
;------------------------------
[SECTION .data]
IDTR:
DW (IDT_END - IDT) - 1
DD IDT
I had also done something very embarrassing in the code which was rotating the accumulator to the right 16 bits in my iteration like this:
After the first iteration, the AX will still have the High Order Word of the __MasterPICHandler procedure and so the consecutive iterations will place wrong values in the IDT. I fixed that problem in this way:
In this way, the __MasterPICHandler is called and the Invalid Opcode Exception is no longer generated by the CPU. One more thing that I did was to replace the HLT at the end of my kernel's code by JMP $. I noticed that when __MasterPICHandler issues the IRET instruction, the CPU will attempt to execute the instruction after HLT which is data and then it generates the Invalid Opcode Exception. Now everything works fine except one thing: the first time a key is pressed, the __MasterPICHandler does its job but the second time, no, nothing, nada, zip. I know that it's because of the JMP $ instruction but how can I work around this? Could somebody please help?
Code: Select all
; Fill IRQ gates in the IDT for the first 8 IRQs
MOV EBX , OFFSET IDT + (0x20 * 0x08)
MOV ECX , 0x08
MOV EAX , OFFSET __MasterPICHandler
@@__FillIRQSlots1to8:
MOV WORD PTR [EBX] , AX
MOV WORD PTR [EBX + 0x02] , 0x0008
MOV WORD PTR [EBX + 0x04] , 0x8F00
ROR EAX , 0x10
MOV WORD PTR [EBX + 0x06] , AX
ADD EBX , 0x08
DEC ECX
JNZ @@__FillIRQSlots1to8
Code: Select all
; Fill IRQ gates in the IDT for the first 8 IRQs
MOV EBX , OFFSET IDT + (0x20 * 0x08) ; EBX points to IDT at byte (0x20 * 0x08)
MOV ECX , 0x08 ; Need to fill 8 IDT Descriptors
MOV EAX , OFFSET __MasterPICHandler ; EAX Points to the Master PIC Procedure
MOV EDX , EAX ; EDX is the same pointer now
SHR EDX , 0x10 ; DX is the High Order Word of this pointer
@@__FillIRQSlots1to8: ; Iteration begins here
MOV WORD PTR [EBX] , AX ; Put the Low Order Word of the pointer
MOV WORD PTR [EBX + 0x02] , 0x0008 ; The Code Segment Descriptor
MOV WORD PTR [EBX + 0x04] , 0x8E00 ; Flags of the Interrupt Gate
MOV WORD PTR [EBX + 0x06] , DX ; Put the High Order Word of the pointer
ADD EBX , 0x08 ; Move to the next Descriptor
DEC ECX ; Decrement the counter
JNZ @@__FillIRQSlots1to8 ; Keep iterating until ECX != 0
; Fill IRQ gates in the IDT for the last 8 IRQs
MOV ECX , 0x08 ; 8 More Descriptors left for the Slave 8259
MOV EAX , OFFSET __SlavePICHandler ; EAX points to the Slave PIC Procedure
MOV EDX , EAX ; EDX is the same pointer now
SHR EDX , 0x10 ; DX is the High Order Word of the pointer
@@__FillIRQSlots9to16: ; The iteration begins here
MOV WORD PTR [EBX] , AX ; Put the Low Order Word of the pointer
MOV WORD PTR [EBX + 0x02] , 0x0008 ; The Code Segment Descriptor
MOV WORD PTR [EBX + 0x04] , 0x8E00 ; Flags of the Interrupt Gate
MOV WORD PTR [EBX + 0x06] , DX ; The High Order Word of the pointer
ADD EBX , 0x08 ; Move to the next Descriptor
DEC ECX ; Decrement the counter
JNZ @@__FillIRQSlots9to16 ; Keep iterating until ECX != 0
MOV AL , 0x01
CALL __EnableInterruptRequest
LIDT [IDTR]
STI
-
- Member
- Posts: 62
- Joined: Tue Feb 13, 2007 10:46 am
Hi,
Basically, the keyboard sends a byte to the keyboard controller, then the keyboard controller puts it in a one byte buffer and sends an IRQ to the CPU to say "byte received". If the CPU doesn't take this byte out of the keyboard controller's buffer, then the next time the keyboard tries to send a byte the buffer is full. In this case the keyboard controller tells the keyboard to retry and doesn't send a new "byte received" IRQ to the CPU because a new byte wasn't received.
Try something like this:
The next problem will be determining which IRQ caused __MasterPICHandler to be called. The best way is to have a seperate routine for each IRQ....
Cheers,
Brendan
That is because you don't get the byte out of the keyboard controller.XCHG wrote:Now everything works fine except one thing: the first time a key is pressed, the __MasterPICHandler does its job but the second time, no, nothing, nada, zip. I know that it's because of the JMP $ instruction but how can I work around this? Could somebody please help?
Basically, the keyboard sends a byte to the keyboard controller, then the keyboard controller puts it in a one byte buffer and sends an IRQ to the CPU to say "byte received". If the CPU doesn't take this byte out of the keyboard controller's buffer, then the next time the keyboard tries to send a byte the buffer is full. In this case the keyboard controller tells the keyboard to retry and doesn't send a new "byte received" IRQ to the CPU because a new byte wasn't received.
Try something like this:
Code: Select all
__MasterPICHandler:
PUSH AX ; <- DON'T TRASH REGISTERS!
IN AL , 0x60 ; <- Get byte from keyboard controller
MOV AL , PIC_EOI
OUT PIC1_COMMAND , AL
POP AX
IRET
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.
Thanks Brendan. My keyboard IRQs are now working and so are the other IRQs. I thought after the keyboard's microcontroller sent the Scan Code to the PC and asserted the IRQ #1, I didn't have to read the Scan Code from port 0x60 anymore. I thought it was useful when polling. I appreciate your help.
Thank you Joe, Frank, Combuster, everyone. Appreciations.
Thank you Joe, Frank, Combuster, everyone. Appreciations.