; =============================================================================
;
; Litos - Interrupt request
;
; =============================================================================
; IRQ chain strategy:
; - "Private handler" cannot share IRQ chain with other handlers, it must
; be installed alone.
; - "Last handler" can share IRQ chain with "share handlers". It is
; installed to the end of IRQ chain. Only one "last handler" can be
; installed in the chain. Interrupt is serviced by "share handlers"
; at first, which can detect its interrupt. If no "share handler"
; catches the interrupt, it is given then to the "last handler".
; - "Dynamic handler" is dynamic allocated handlers, which is activated
; only shortly when needed it. It can share IRQ chain with "share
; handlers" (not with "last handler") and is is installed to the end
; of IRQ chain, similary as "last handler". More dynamic handlers can
; be installed in one IRQ chain, but only one of them can be active at
; a time.
; - "Share handler" can share IRQ chain with other handlers. It is
; installed from begin of IRQ chain. It can detect its own interrupt
; using fast handler. If it is not its own interrupt, it returns
; error flag and the interrupt is given to the next handler. Handlers
; with "traffic" modification flag are chained before other ones.
;
; IRQ service uses two types of interrupt handlers. The first one is "fast"
; handler. It is called immediately when interrupt comes, with interrupts
; disabled and with locked 8259A interrupt controller. It must work quickly
; as possible and it may use functions "called from interrupt handler".
; "Share handler" must return error flag CY if it is not its own interrupt.
; IRQ are counted and then (in scheduler) the slow handler is called, which
; has interrupts enabled and 8259A interrupt controller unlocked.
;
; Default IRQ assignments on PC/AT:
; Master 8259:
; IRQ 0: programmable interval timer (PIT)
; IRQ 1: keyboard, mouse, RTC interrupt
; IRQ 2: cascade to slave 8259 INT line, video interrupt
; IRQ 3: serial port COM2 and COM4
; IRQ 4: serial port COM1 and COM3
; IRQ 5: LPT2, sound card, fixed disk controller
; IRQ 6: floppy disk controller
; IRQ 7: LPT1 (or sound card, older usage)
; Slave 8259:
; IRQ 8: real-time clock (RTC)
; IRQ 9: MPU-401 MID port, ACPI SCI, redirect cascade
; IRQ 10:
; IRQ 11:
; IRQ 12: PS/2 mouse
; IRQ 13: math coprocessor
; IRQ 14: hard disk controller 1
; IRQ 15: hard disk controller 2
; =============================================================================
CODE_SECTION 32
; -----------------------------------------------------------------------------
; Lock/unlock IRQ
; -----------------------------------------------------------------------------
; NOTES: Use macro IRQLOCK to lock, IRQUNLOCK to unlock.
; -----------------------------------------------------------------------------
; ------------- IRQ lock function
%ifdef SMP
LOCK_LockFnc IRQLock,Lock8259A ; declare IRQ lock function
%endif
; ------------- Macro - call IRQ lock function
%macro IRQLOCK 0
%ifdef SMP
call IRQLock ; call IRQ lock function
%endif
%endmacro
; ------------- Macro - IRQ unlock
%macro IRQUNLOCK 0
%ifdef SMP
LOCK_Unlock Lock8259A ; unlock 8259A
%endif
%endmacro
; -----------------------------------------------------------------------------
; Verify IRQ handler if can be installed into specific descriptor
; -----------------------------------------------------------------------------
; INPUT: EBX = installed IRQ handler
; EDX = IRQ descriptor
; OUTPUT: CY = handler cannot be installed
; NOTES: This is local function for IRQInstall.
; -----------------------------------------------------------------------------
; ------------- Push registers
IRQInstTest: push eax ; push EAX
push ecx ; push ECX
; ------------- Check handler mask of usable IRQs
movzx eax,byte [edx+IRQDESC_IRQ] ; EAX <- IRQ number
bt [ebx+IRQHAND_Mask],eax ; check IRQ mask
jnc IRQInstTest8 ; this IRQ is not enabled
; ------------- "No handlers" is suitable for all the time
cmp dword [edx+IRQDESC_Total],byte 0 ; any other handlers?
je IRQInstTest9 ; no handlers, it satisfies
; ------------- Private handler cannot be shared (AH <- flags of this handler)
mov ah,[ebx+IRQHAND_Flags] ; AH <- flags
test ah,IRQ_PRIVATE ; install private handler?
jnz IRQInstTest8 ; private handler does not satisfy
; ------------- Get flags of last handler (-> AL)
mov ecx,[edx+LIST_Prev] ; ECX <- last handler
mov al,[ecx+IRQHAND_Flags] ; AL <- flags
; ------------- Cannot share private handler
test al,IRQ_PRIVATE ; private handler?
jnz IRQInstTest8 ; cannot share private handler
; ------------- Check last handler
test ah,IRQ_LAST ; install last handler?
jz IRQInstTest2 ; not last handler
test al,IRQ_LAST|IRQ_DYNAM ; last or dynamic handler?
jnz IRQInstTest8 ; it already has last/dynamic handler
jmp short IRQInstTest9 ; this handler can be installed OK
; ------------- Check dynamic handler
IRQInstTest2: test ah,IRQ_DYNAM ; install dynamic handler?
jz IRQInstTest9 ; not dynamic handler, share one is OK
test al,IRQ_LAST ; last handler?
jz IRQInstTest9 ; no last handler, it can be installed
; ------------- Error, handler cannot be installed
IRQInstTest8: stc ; set error flag
; ------------- Pop registers
IRQInstTest9: pop ecx ; pop ECX
pop eax ; pop EAX
ret
; -----------------------------------------------------------------------------
; Verify and install IRQ handler
; -----------------------------------------------------------------------------
; INPUT: EBX = installed IRQ handler
; EDX = IRQ descriptor
; OUTPUT: CY = handler cannot be installed
; NOTES: This is local function for IRQInstall.
; -----------------------------------------------------------------------------
; ------------- Check if IRQ handler can be installed
IRQInstInst: call IRQInstTest ; check if IRQ handler can be installed
jc IRQInstInst9 ; handler cannot be installed
; ------------- Push registers
push eax ; push EAX
; ------------- Prepare installed handler (-> EAX) and list header (-> EBX)
xchg eax,ebx ; EAX <- installed IRQ handler
mov ebx,edx ; EBX <- IRQ descriptor
; ------------- Check if it is firt handler
cmp dword [edx+IRQDESC_Total],byte 0 ; any handlers?
je IRQInstInst4 ; it is first handler
; ------------- Check if it is share handler
test byte [eax+IRQHAND_Flags],IRQ_SHARE ; share handler?
jz IRQInstInst4 ; not share handler
; ------------- Check if it is traffic handler
test byte [eax+IRQHAND_Flags],IRQ_TRAFFIC ; traffic?
jz IRQInstInst2 ; it is not traffic handler
; ------------- Install traffic handler to the begin of IRQ chain
call ListAfter ; install at start of IRQ chain
jmp short IRQInstInst6
; ------------- Find last traffic handler
IRQInstInst2: mov ebx,[ebx+LIST_Next] ; EBX <- next handler
cmp ebx,edx ; is it last handler?
je IRQInstInst4 ; it is last handler
test byte [ebx+IRQHAND_Flags],IRQ_SHARE ; share handler?
jz IRQInstInst4 ; found non-shared handler
test byte [ebx+IRQHAND_Flags],IRQ_TRAFFIC ; non-traffic?
jnz IRQInstInst2 ; shared traffic, get next handler
; ------------- Install normal handlers at the end of IRQ chain
IRQInstInst4: call ListBefore ; install at end of IRQ chain
IRQInstInst6: xchg eax,ebx ; EBX <- installed IRQ handler
; ------------- Count IRQ handlers
inc dword [edx+IRQDESC_Total] ; increase number of handlers
; ------------- Initialize handler
mov [ebx+IRQHAND_Desc],edx ; set pointer to IRQ descriptor
mov al,[edx+IRQDESC_IRQ] ; AL <- IRQ number
mov [ebx+IRQHAND_IRQ],al ; set IRQ number
and dword [ebx+IRQHAND_Count],byte 0 ; reset slow counter
; ------------- Activate IRQ
btr dword [ebx+IRQHAND_Flags],IRQ_ACTIVE_BIT ; active?
jnc IRQInstInst8 ; should not be active
call IRQIntAct ; activate IRQ
; ------------- Pop registers
IRQInstInst8: pop eax ; pop EAX
clc ; flag, installation OK
IRQInstInst9: ret
; -----------------------------------------------------------------------------
; Install IRQ handler
; -----------------------------------------------------------------------------
; INPUT: EBX = IRQ handler IRQHAND with filled up entries
; OUTPUT: EAX = IRQ number (or -1 on error)
; CY = cannot install IRQ handler (no free IRQ entry)
; NOTES: It activates IRQ handler if it had active flag.
; -----------------------------------------------------------------------------
; TODO: Try to reorganize IRQ handlers if no acceptable IRQ found.
; ------------- Push registers
IRQInstall: push ecx ; push ECX
push edx ; push EDX
; ------------- Disable interrupts
pushf ; push flags
cli ; disable interrupts
; ------------- Lock 8259A interrupt controller
IRQLOCK ; lock 8259A interrupt controller
; ------------- Try to install into the best IRQ
movzx edx,byte [ebx+IRQHAND_Best] ; EDX <- best IRQ number
cmp dl,IRQ_NUM ; check valid IRQ number
jae IRQInst2 ; IRQ number is not valid
shl edx,3 ; EDX <- IRQ number * 8
lea edx,[IRQTab+edx*(IRQDESC_size/8)] ; EDX <- descriptor
call IRQInstInst ; try to install handler
jnc IRQInst9 ; handler installed OK
; ------------- Find next minimal number of handlers
IRQInst2: xor ecx,ecx ; ECX <- 0, acceptable minimum
IRQInst22: xor eax,eax ; EAX <- 0
dec eax ; EAX <- 0ffffffffh (maximum)
mov edx,IRQTab ; EDX <- IRQ descriptor table
IRQInst24: cmp ecx,[edx+IRQDESC_Total] ; is this minimum acceptable?
ja IRQInst26 ; descriptor is not acceptable
cmp eax,[edx+IRQDESC_Total] ; smaller number of handlers?
jbe IRQInst26 ; it is not smaller
mov eax,[edx+IRQDESC_Total] ; EAX <- new smaller number
IRQInst26: add edx,byte IRQDESC_size ; EDX <- next IRQ descriptor
cmp edx,IRQTab+IRQ_NUM*IRQDESC_size ; end of table?
jb IRQInst24 ; get next entry
inc eax ; test if found next entry
jz IRQInst8 ; no next entry found
dec eax ; EAX = minimal number of handlers
; ------------- Try to install into descriptor with minimal handlers
mov edx,IRQTab ; EDX <- IRQ descriptor table
IRQInst42: cmp eax,[edx+IRQDESC_Total] ; is it acceptable descriptor?
jne IRQInst44 ; descriptor is not acceptable
call IRQInstInst ; try to install handler
jnc IRQInst9 ; handler installed OK
IRQInst44: add edx,byte IRQDESC_size ; EDX <- next IRQ descriptor
cmp edx,IRQTab+IRQ_NUM*IRQDESC_size ; end of table?
jb IRQInst42 ; get next entry
inc eax ; EAX <- handlers + 1
xchg eax,ecx ; ECX <- new acceptable minimum
jmp short IRQInst22 ; test next number of handlers
; ------------- Installation ERROR: unlock 8259A interrupt controller
IRQInst8: IRQUNLOCK ; unlock 8259A interrupt controller
; ------------- Pop registers
popf ; pop flags
pop edx ; pop EDX
pop ecx ; pop ECX
xor eax,eax ; EAX <- 0
dec eax ; EAX <- -1
stc ; flag, installation ERROR
ret
; ------------- Installation OK: unlock 8259A interrupt controller
IRQInst9: IRQUNLOCK ; unlock 8259A interrupt controller
; ------------- Pop registers
popf ; pop flags
pop edx ; pop EDX
pop ecx ; pop ECX
movzx eax,byte [ebx+IRQHAND_IRQ] ; EAX <- IRQ number
clc ; flag, installation OK
ret
; -----------------------------------------------------------------------------
; Uninstall IRQ handler
; -----------------------------------------------------------------------------
; INPUT: EBX = IRQ handler
; NOTES: It deactivates IRQ handler if it was active.
; -----------------------------------------------------------------------------
; ------------- Push registers
IRQUninstall: push ecx ; push ECX
push edx ; push EDX
; ------------- Disable interrupts
pushf ; push flags
cli ; disable interrupts
; ------------- Lock 8259A interrupt controller
IRQLOCK ; lock 8259A interrupt controller
; ------------- Deactivate IRQ handler
call IRQIntDeact ; deactivate IRQ handler
; ------------- Free IRQ handler from the list
call ListDelEBX ; free IRQ handler
; ------------- Get IRQ descriptor (-> EDX)
mov edx,[ebx+IRQHAND_Desc] ; EDX <- IRQ descriptor
; ------------- Subtract counter of slow handler
xor ecx,ecx ; ECX <- 0
xchg ecx,[ebx+IRQHAND_Count] ; ECX <- counter
jecxz IRQUninst4 ; no pending handler
sub [edx+IRQDESC_Count],ecx ; decrease counter
jnz IRQUninst4 ; other handlers are pending
movzx ecx,byte [edx+IRQDESC_IRQ] ; ECX <- IRQ number
btr [IRQSlowMask],ecx ; reset bit flag
; ------------- Unlock 8259A interrupt controller
IRQUninst4: IRQUNLOCK ; unlock 8259A interrupt controller
; ------------- Pop registers
popf ; pop flags
pop edx ; pop EDX
pop ecx ; pop ECX
ret
; -----------------------------------------------------------------------------
; Activate fast IRQ handler
; -----------------------------------------------------------------------------
; INPUT: EBX = IRQ handler
; -----------------------------------------------------------------------------
; ------------- Fast check if IRQ handler is already active
IRQAct: test byte [ebx+IRQHAND_Flags],IRQ_ACTIVE ; active?
jnz IRQAct9 ; handler is already active
; ------------- Push registers
push eax ; push EAX
; ------------- Disable interrupts
pushf ; push flags
cli ; disable interrupts
; ------------- Lock 8259A interrupt controller
IRQLOCK ; lock 8259A interrupt controller
; ------------- Check if IRQ handler is still inactive and activate it
bts dword [ebx+IRQHAND_Flags],IRQ_ACTIVE_BIT ; active?
jc IRQAct8 ; handler is already active
; ------------- Count number of active fast handlers
mov eax,[ebx+IRQHAND_Desc] ; EAX <- IRQ descriptor
inc dword [eax+IRQDESC_Active] ; increase active handlers
; ------------- Enable IRQ
mov al,[eax+IRQDESC_IRQ] ; AL <- IRQ number
call IRQIntEnable ; enable IRQ
; ------------- Unlock 8259A interrupt controller
IRQAct8: IRQUNLOCK ; unlock 8259A interrupt controller
; ------------- Pop registers
popf ; pop flags
pop eax ; pop EAX
IRQAct9: ret
; -----------------------------------------------------------------------------
; Activate fast IRQ handler, called from interrupt handler
; -----------------------------------------------------------------------------
; INPUT: EBX = IRQ handler
; -----------------------------------------------------------------------------
; ------------- Activate IRQ handler
IRQIntAct: bts dword [ebx+IRQHAND_Flags],IRQ_ACTIVE_BIT ; active?
jc IRQIntAct9 ; handler is already active
; ------------- Push registers
push eax ; push EAX
; ------------- Count number of active fast handlers
mov eax,[ebx+IRQHAND_Desc] ; EAX <- IRQ descriptor
inc dword [eax+IRQDESC_Active] ; increase active handlers
; ------------- Enable IRQ
mov al,[eax+IRQDESC_IRQ] ; AL <- IRQ number
call IRQIntEnable ; enable IRQ
; ------------- Pop registers
pop eax ; pop EAX
IRQIntAct9: ret
; -----------------------------------------------------------------------------
; Deactivate fast IRQ handler
; -----------------------------------------------------------------------------
; INPUT: EBX = IRQ handler
; -----------------------------------------------------------------------------
; ------------- Fast check if IRQ handler is already inactive
IRQDeact: test byte [ebx+IRQHAND_Flags],IRQ_ACTIVE ; active?
jz IRQDeact9 ; handler is already inactive
; ------------- Push registers
push eax ; push EAX
; ------------- Disable interrupts
pushf ; push flags
cli ; disable interrupts
; ------------- Lock 8259A interrupt controller
IRQLOCK ; lock 8259A interrupt controller
; ------------- Check if IRQ handler is still active and deactivate it
btr dword [ebx+IRQHAND_Flags],IRQ_ACTIVE_BIT ; active?
jnc IRQDeact8 ; handler is already inactive
; ------------- Count number of active fast handlers
mov eax,[ebx+IRQHAND_Desc] ; EAX <- IRQ descriptor
dec dword [eax+IRQDESC_Active] ; decrease active handlers
jnz IRQDeact8 ; other handlers remain
; ------------- Disable IRQ
mov al,[eax+IRQDESC_IRQ] ; AL <- IRQ number
call IRQIntDisable ; disable IRQ
; ------------- Unlock 8259A interrupt controller
IRQDeact8: IRQUNLOCK ; unlock 8259A interrupt controller
; ------------- Pop registers
popf ; pop flags
pop eax ; pop EAX
IRQDeact9: ret
; -----------------------------------------------------------------------------
; Deactivate fast IRQ handler, called from interrupt handler
; -----------------------------------------------------------------------------
; INPUT: EBX = IRQ handler
; -----------------------------------------------------------------------------
; ------------- Deactivate IRQ handler
IRQIntDeact: btr dword [ebx+IRQHAND_Flags],IRQ_ACTIVE_BIT ; active?
jnc IRQIntDeact9 ; handler is already inactive
; ------------- Push registers
push eax ; push EAX
; ------------- Count number of active fast handlers
mov eax,[ebx+IRQHAND_Desc] ; EAX <- IRQ descriptor
dec dword [eax+IRQDESC_Active] ; decrease active handlers
jnz IRQIntDeact8 ; other handlers remain
; ------------- Disable IRQ
mov al,[eax+IRQDESC_IRQ] ; AL <- IRQ number
call IRQIntDisable ; disable IRQ
; ------------- Pop registers
IRQIntDeact8: pop eax ; pop EAX
IRQIntDeact9: ret
; -----------------------------------------------------------------------------
; Acknowledge IRQ, called from interrupt handler
; -----------------------------------------------------------------------------
; INPUT: AL = IRQ number (0 to 15)
; NOTES: AL not checked for validity
; -----------------------------------------------------------------------------
; ------------- Push registers
IRQIntAck: push eax ; push EAX
mov ah,al ; AH <- IRQ number
; ------------- Release controller 1
in al,21h ; release controller 1
; ------------- Prepare command for controller 1
mov al,60h ; AL <- command
add al,ah ; add IRQ number
; ------------- Check if use controller 2
cmp ah,8 ; use controller 2?
jb IRQIntAck4 ; use controller 1
; ------------- Release controller 2
in al,0a1h ; release controller 2
; ------------- Acknowledge interrupt on controller 2
mov al,60h-8 ; AL <- command
add al,ah ; add IRQ number
out 0a0h,al ; acknowledge interrupt
; ------------- Acknowledge interrupt on controller 1
mov al,62h ; AL <- command, IRQ 2
IRQIntAck4: out 20h,al ; acknowledge interrupt
; ------------- Pop registers
pop eax ; pop EAX
ret
; -----------------------------------------------------------------------------
; Enable IRQ, called from interrupt handler
; -----------------------------------------------------------------------------
; INPUT: AL = IRQ number (0 to 15)
; NOTES: AL not checked for validity
; -----------------------------------------------------------------------------
; ------------- Push registers
IRQIntEnable: push eax ; push EAX
push ebx ; push EBX
; ------------- Fast check if IRQ is already enabled
movzx eax,al ; EAX <- IRQ number
mov ebx,IRQMask ; EBX <- IRQ mask
bt [ebx],eax ; is IRQ already enabled?
jnc IRQIntEnable8 ; it is already enabled
; ------------- Reset IRQ mask
btr [ebx],eax ; reset IRQ mask
; ------------- Check if use controller 2
IRQIntEnable2: cmp al,8 ; use controller 2?
mov eax,[ebx] ; AX <- current mask 1 and 2
jae IRQIntEnable4 ; use controller 2
; ------------- Send mask to controller 1 (MASTER)
out 21h,al ; send interrupt mask 1
jmp short IRQIntEnable8
; ------------- Send mask to controller 2 (SLAVE)
IRQIntEnable4: mov al,ah ; AL <- interrupt mask 2
out 0a1h,al ; send interrupt mask 2
; ------------- Pop registers
IRQIntEnable8: pop ebx ; pop EBX
pop eax ; pop EAX
ret
; -----------------------------------------------------------------------------
; Disable IRQ, called from interrupt handler
; -----------------------------------------------------------------------------
; INPUT: AL = IRQ number (0 to 15)
; NOTES: AL not checked for validity
; -----------------------------------------------------------------------------
; ------------- Push registers
IRQIntDisable: push eax ; push EAX
push ebx ; push EBX
; ------------- Fast check if IRQ is already disabled
movzx eax,al ; EAX <- IRQ number
mov ebx,IRQMask ; EBX <- IRQ mask
bt [ebx],eax ; is IRQ already disabled?
jc IRQIntEnable8 ; it is already disabled
; ------------- Set IRQ mask
bts [ebx],eax ; set IRQ mask
jmp short IRQIntEnable2
; -----------------------------------------------------------------------------
; Enable IRQ
; -----------------------------------------------------------------------------
; INPUT: AL = IRQ number (0 to 15)
; NOTES: AL not checked for validity
; -----------------------------------------------------------------------------
; ------------- Push registers
IRQEnable: push eax ; push EAX
push ebx ; push EBX
; ------------- Fast check if IRQ is already enabled
movzx eax,al ; EAX <- IRQ number
mov ebx,IRQMask ; EBX <- IRQ mask
bt [ebx],eax ; is IRQ already enabled?
jnc IRQEnable8 ; it is already enabled
; ------------- Disable interrupts
pushf ; push flags
cli ; disable interrupts
; ------------- Lock 8259A interrupt controller
IRQLOCK ; lock 8259A interrupt controller
; ------------- Reset IRQ mask
btr [ebx],eax ; reset IRQ mask
; ------------- Check if use controller 2
IRQEnable2: cmp al,8 ; use controller 2?
mov eax,[ebx] ; AX <- current mask 1 and 2
jae IRQEnable4 ; use controller 2
; ------------- Send mask to controller 1 (MASTER)
out 21h,al ; send interrupt mask 1
jmp short IRQEnable6
; ------------- Send mask to controller 2 (SLAVE)
IRQEnable4: mov al,ah ; AL <- interrupt mask 2
out 0a1h,al ; send interrupt mask 2
; ------------- Unlock 8259A interrupt controller
IRQEnable6: IRQUNLOCK ; unlock 8259A interrupt controller
; ------------- Pop registers
popf ; pop flags
IRQEnable8: pop ebx ; pop EBX
pop eax ; pop EAX
ret
; -----------------------------------------------------------------------------
; Disable IRQ
; -----------------------------------------------------------------------------
; INPUT: AL = IRQ number (0 to 15)
; NOTES: AL not checked for validity
; -----------------------------------------------------------------------------
; ------------- Push registers
IRQDisable: push eax ; push EAX
push ebx ; push EBX
; ------------- Fast check if IRQ is already disabled
movzx eax,al ; EAX <- IRQ number
mov ebx,IRQMask ; EBX <- IRQ mask
bt [ebx],eax ; is IRQ already disabled?
jc IRQEnable8 ; it is already disabled
; ------------- Disable interrupts
pushf ; push flags
cli ; disable interrupts
; ------------- Lock 8259A interrupt controller
IRQLOCK ; lock 8259A interrupt controller
; ------------- Set IRQ mask
bts [ebx],eax ; set IRQ mask
jmp short IRQEnable2
; -----------------------------------------------------------------------------
; Check if IRQ is pending, called from interrupt handler
; -----------------------------------------------------------------------------
; INPUT: AL = IRQ number (0 to 15)
; OUTPUT: CY = IRQ is pending
; NOTES: AL not checked for validity
; -----------------------------------------------------------------------------
; ------------- Push registers
IRQIntCheck: push eax ; push EAX
push ecx ; push ECX
; ------------- Prepare IRQ number
movzx ecx,al ; ECX <- IRQ number
; ------------- Check if use controller 2
cmp al,8 ; use controller 2?
jae IRQIntCheck2 ; use controller 2
; ------------- Get request mask from controller 1 (MASTER)
in al,20h ; get request mask 1
jmp short IRQIntCheck4
; ------------- Get request mask from controller 2 (SLAVE)
IRQIntCheck2: in al,0a0h ; get request mask 2
mov ah,al ; AH <- request mask 2
; ------------- Check if IRQ is pending
IRQIntCheck4: bt eax,ecx ; check if IRQ is pending
; ------------- Pop registers
pop ecx ; pop ECX
pop eax ; pop EAX
ret
; -----------------------------------------------------------------------------
; Check if IRQ is pending (read interrupt request register IRR)
; -----------------------------------------------------------------------------
; INPUT: AL = IRQ number (0 to 15)
; OUTPUT: CY = IRQ is pending
; NOTES: AL not checked for validity
; -----------------------------------------------------------------------------
; ------------- Push registers
IRQCheck: push eax ; push EAX
push ecx ; push ECX
pushf ; push flags
cli ; interrupts must be realy disabled
; ------------- Lock 8259A interrupt controller
IRQLOCK ; lock 8259A interrupt controller
; ------------- Prepare IRQ number
movzx ecx,al ; ECX <- IRQ number
; ------------- Check if use controller 2
cmp al,8 ; use controller 2?
jae IRQCheck2 ; use controller 2
; ------------- Get request mask from controller 1 (MASTER)
in al,20h ; get request mask 1
jmp short IRQCheck4
; ------------- Get request mask from controller 2 (SLAVE)
IRQCheck2: in al,0a0h ; get request mask 2
mov ah,al ; AH <- request mask 2
; ------------- Unlock 8259A interrupt controller
IRQCheck4: IRQUNLOCK ; unlock 8259A interrupt controller
; ------------- Pop registers
popf ; pop flags
bt eax,ecx ; check if IRQ is pending
pop ecx ; pop ECX
pop eax ; pop EAX
ret
; -----------------------------------------------------------------------------
; Get IRQ interrupt in-service of IRQ, called from interrupt handler
; -----------------------------------------------------------------------------
; INPUT: AL = IRQ number (0 to 15)
; OUTPUT: CY = IRQ is in-service
; NOTES: AL not checked for validity
; -----------------------------------------------------------------------------
; ------------- Push registers
IRQIntServ: push eax ; push EAX
push ecx ; push ECX
push edx ; push EDX
; ------------- Prepare port address amd IRQ number
mov dx,20h ; DX <- MASTER port
cmp al,8 ; use controller 1?
jb IRQIntServ2 ; use controller 1
mov dl,0a0h ; DX <- SLAVE port
sub al,8 ; AL <- relative IRQ number
IRQIntServ2: movzx ecx,al ; ECX <- relative IRQ number
; ------------- Switch controller to ISR (interrupt in-service register)
mov al,B3+3 ; in-service mode
out dx,al ; set OCW2
SHORT_DELAY ; short delay
; ------------- Check in-service state (-> CF)
in al,dx ; get in-service mask
bt eax,ecx ; check if IRQ is in-service
; ------------- Switch controller back to IRR (interrupt request register)
mov al,B3+2 ; request mode
out dx,al ; set OCW2
; ------------- Pop registers
pop edx ; pop EDX
pop ecx ; pop ECX
pop eax ; pop EAX
ret
; -----------------------------------------------------------------------------
; Get IRQ interrupt in-service of IRQ (register ISR)
; -----------------------------------------------------------------------------
; INPUT: AL = IRQ number (0 to 15)
; OUTPUT: CY = IRQ is in-service
; NOTES: AL not checked for validity
; -----------------------------------------------------------------------------
; ------------- Push registers
IRQServ: push eax ; push EAX
push ecx ; push ECX
push edx ; push EDX
pushf ; push flags
cli ; interrupts must be realy disabled
; ------------- Lock 8259A interrupt controller
IRQLOCK ; lock 8259A interrupt controller
; ------------- Prepare port address amd IRQ number
mov dx,20h ; DX <- MASTER port
cmp al,8 ; use controller 1?
jb IRQServ2 ; use controller 1
mov dl,0a0h ; DX <- SLAVE port
sub al,8 ; AL <- relative IRQ number
IRQServ2: movzx ecx,al ; ECX <- relative IRQ number
; ------------- Switch controller to ISR (interrupt in-service register)
mov al,B3+3 ; in-service mode
out dx,al ; set OCW2
SHORT_DELAY ; short delay
; ------------- Get in-service state (-> AH)
in al,dx ; get in-service mask
mov ah,al ; AH <- push mask
; ------------- Switch controller back to IRR (interrupt request register)
mov al,B3+2 ; request mode
out dx,al ; set OCW2
mov al,ah ; AL <- pop mask
; ------------- Unlock 8259A interrupt controller
IRQUNLOCK ; unlock 8259A interrupt controller
; ------------- Pop registers
popf ; pop flags
bt eax,ecx ; check if IRQ is in-service
pop edx ; pop EDX
pop ecx ; pop ECX
pop eax ; pop EAX
ret
; -----------------------------------------------------------------------------
; Run slow interrupt handlers
; -----------------------------------------------------------------------------
; ------------- Check if there is some handler pending
IRQRunSlow: cmp dword [IRQSlowMask],byte 0 ; any handler?
je IRQRunSlow9 ; no slow handler pending
; ------------- Push registers
pusha ; push all registers
pushf ; push flags
cli ; disable interrupts
; ------------- Lock 8259A interrupt controller
IRQLOCK ; lock 8259A interrupt controller
; ------------- Get IRQ with next pending handler (-> EAX)
mov edx,[IRQSlowMask] ; EDX <- mask of slow handlers
bsf eax,edx ; search first pending handler
jz IRQRunSlow8 ; no handler pending
; ------------- IRQ descriptor (-> EBX)
mov ebx,eax ; EBX <- IRQ number
shl ebx,3 ; EBX <- IRQ number * 8
lea ebx,[IRQTab+ebx*(IRQDESC_size/8)]
; ------------- Walk through the list of handlers
mov edx,ebx ; EDX <- list header
IRQRunSlow2: mov edx,[edx+LIST_Next] ; EDX <- next handler
; ------------- Check if there is pending slow handler
xor ecx,ecx ; ECX <- 0
xchg ecx,[edx+IRQHAND_Count] ; ECX <- counter
jecxz IRQRunSlow2 ; no pending handler
; ------------- Decrease counter
sub [ebx+IRQDESC_Count],ecx ; decrease counter
jnz IRQRunSlow4 ; other handlers are pending
btr [IRQSlowMask],eax ; reset bit flag
; ------------- Get user data and function address
IRQRunSlow4: mov ebx,[edx+IRQHAND_Data] ; EBX <- user data
mov esi,[edx+IRQHAND_Slow] ; ESI <- slow function
; ------------- Unlock 8259A interrupt controller
IRQUNLOCK ; unlock 8259A interrupt controller
popf ; pop flags
; ------------- Call slow handler
or esi,esi ; any slow handler?
jz IRQRunSlow6 ; no slow handler
call esi ; call slow handler
IRQRunSlow6: popa ; pop all registers
jmp short IRQRunSlow ; next handler
; ------------- Unlock 8259A interrupt controller
IRQRunSlow8: IRQUNLOCK ; unlock 8259A interrupt controller
; ------------- Pop registers
popf ; pop flags
popa ; pop all registers
IRQRunSlow9: ret
; -----------------------------------------------------------------------------
; IRQ services (IRQ 1 to 15, i.e. INT 33 to 47)
; -----------------------------------------------------------------------------
; NOTES: IRQ 0 (INT 32) is used by system timer
; -----------------------------------------------------------------------------
; ------------- IRQ 2 service
IRQ2: IRQLOCK ; lock 8259A interrupt controller
push eax ; push EAX
mov al,2 ; AL <- IRQ number
call IRQIntAck ; acknowledge interrupt
pop eax ; pop EAX
IRQUNLOCK ; unlock 8259A interrupt controller
iret
; ------------- IRQ head (push registers and prepare IRQ number)
%assign IRQN 3
%rep IRQ_NUM-4
IRQ %+ IRQN:
pusha ; push all registers
mov al,IRQN ; AL <- IRQ number
jmp short IRQInt ; IRQ common service
%assign IRQN IRQN+1
%endrep
; ------------- IRQ 15 head
IRQ15: pusha ; push all registers
mov al,15 ; AL <- IRQ number
; ------------- Push other registers (here AL = IRQ number)
IRQInt: push ds ; push DS
push es ; push ES
cld ; direction up
; ------------- Initialize kernel segments
mov ebx,SYSTEM_DS ; EBX <- system DS
mov ds,ebx ; DS <- system DS
mov es,ebx ; ES <- system DS
; ------------- Lock 8259A interrupt controller
IRQLOCK ; lock 8259A interrupt controller
; ------------- Acknowledge interrupt
call IRQIntAck ; acknowledge interrupt
; ------------- IRQ descriptor (-> EBX)
movzx ecx,al ; ECX <- IRQ number
shl ecx,3 ; ECX <- IRQ number * 8
lea ecx,[IRQTab+ecx*(IRQDESC_size/8)]
; ------------- Walk through the list of handlers
mov edx,ecx ; EDX <- list header
IRQInt2: mov ecx,[ecx+LIST_Next] ; ECX <- next handler
cmp ecx,edx ; end of list?
je IRQInt6 ; end of list
; ------------- Check if fast handler is active
test byte [ecx+IRQHAND_Flags],IRQ_ACTIVE ; active?
jz IRQInt2 ; handler is not active
; ------------- Check if fast handler is valid
mov esi,[ecx+IRQHAND_Fast] ; ESI <- fast handler
or esi,esi ; is fast handler valid?
jz IRQInt3 ; there is no fast handler
; ------------- Call fast handler
mov ebx,[ecx+IRQHAND_Data] ; EBX <- user data
call esi ; call fast handler
jnc IRQInt3 ; service is OK
; ------------- Not own interrupt, check if it is shared IRQ
test byte [ecx+IRQHAND_Flags],IRQ_SHARE ; shared IRQ?
jnz IRQInt2 ; shared IRQ, test next handler
; ------------- Check if it has slow handler
IRQInt3: cmp dword [ecx+IRQHAND_Slow],byte 0; is slow handler valid?
je IRQInt6 ; slow handler is not valid
; ------------- Increase counter for slow interrupts
inc dword [ecx+IRQHAND_Count] ; increase slow counter
inc dword [edx+IRQDESC_Count] ; increase slow counter
bts [IRQSlowMask],eax ; set slow handler flag
; ------------- Reschedule current CPU
CURRENT esi ; ESI <- get current task
mov esi,[esi+TASK_RunQueue] ; ESI <- current run-queue
RESCHEDULE esi ; reschedule request
; ------------- Unlock 8259A interrupt controller
IRQInt6: IRQUNLOCK ; unlock 8259A interrupt controller
; ------------- Pop registers
pop es ; pop ES
pop ds ; pop DS
popa ; pop all registers
iret
; ------------- IRQ service jump table
IRQJumpTab: dd TimerInt ; IRQ 0: system timer
dd KeybInt ; IRQ 1: keyboard
%assign IRQN 2
%rep IRQ_NUM-2
dd IRQ %+ IRQN
%assign IRQN IRQN+1
%endrep
; -----------------------------------------------------------------------------
; Initialize/reinitialize 8259A interrupt controller
; -----------------------------------------------------------------------------
; INPUT: AL = B1: enable auto EOI mode (other bits must be cleared)
; -----------------------------------------------------------------------------
; 8259A initialization command word ICW1 (write to port 20h/0A0h):
; B0: 1=ICW4 needed, 0=no ICW4
; B1: 0=cascade mode, ICW3 needed
; B2: 0=call address interval of 8 bytes
; B3: 0=edge triggered mode
; B4: 1
; B5..B7: 0 (interrupt vector address)
; 8259A initialization command word ICW2 (write to port 21h/0A1h after ICW1):
; B0..B3: 0
; B3..B7: A3..A7 of interrupt vector address
; 8259A initialization command word ICW3 for MASTER (write to 21h after ICW2):
; B0..B7: 1=input has slave (must be = 04h)
; 8259A initialization command word ICW3 for SLAVE (write to 0A1h after ICW2):
; B0..B2: line with slave 0..7 (must be = 02h)
; B3..B7: 0
; 8259A initialization command word ICW4 (write to port 21h/0A1h after ICW3):
; B0: 1=8086/8088 mode
; B1: 1=auto EIO, 0=normal EOI
; B2,B3: 00 and 01 = non buffered mode
; 10 = buffered mode, slave
; 11 = buffered mode, master
; B4: 1=special fully nested mode, 0=not special fully nested mode
; B5..B7: 0
;
; 8259A operation control word OCW1 (read/write to port 21h/0A1h):
; B0..B7: interrupt mask register, 1=interrupt is disabled, 0=enabled
; 8259A operation control word OCW2 (write to port 20h/0A0h):
; B0..B2: IRQ number to with the command applies (0 to 7)
; B3,B4: 0
; B5..B7: operation
; 010 no operation (=40h)
; end of interrupt:
; 001 = non-specific EOI command (=20h)
; 011 = specific EOI command (=60h to 67h)
; automatic rotation:
; 101 = rotate on non-specific EOI command (=0A0h)
; 100 = rotate in automatic EOI mode (set) (=80h)
; 000 = rotate in automatic EOI mode (clear) (=0)
; specific rotation:
; 111 = rotate on specific EOI command (=0E0h to 0E7h)
; 110 = set priority command (=0C0h to 0C7h)
; 8259A operation control word OCW3 (write to port 20h/0A0h):
; B0,B1: 00,01 = no operation
; 10 = read interrupt request register on next read from 20h/0A0h
; 11 = read interrupt in-service reg. on next read from 20h/0A0h
; B2: 1=poll command, 0=no poll command
; B3: 1
; B4: 0
; B5,B6: 00,01 = no operation
; 10 = reset special mask
; 11 = set special mask
; B7: 0
; 8259A: interrupt request register (read from 20h/0A0h after set OCW3 to IRR)
; B0..B7: 1=active request for corresponding interrupt line, 0=no request
; 8259A: interrupt request register (read from 20h/0A0h after set OCW3 to ISR)
; B0..B7: 1=corresponding interrupt line currently being service, 0=no
; -----------------------------------------------------------------------------
; ------------- Push registers
Init8259A: push eax ; push EAX
mov ah,al ; AH <- push AL
pushf ; push flags
cli ; interrupts must be realy disabled
; ------------- Lock 8259A interrupt controller
IRQLOCK ; lock 8259A interrupt controller
; ------------- Disable all interrupts
mov al,0ffh ; AL <- interrupt mask
out 0a1h,al ; disable all interrupts
out 21h,al ; disable all interrupts
; ------------- Set MASTER control word ICW1 (cascade mode, level triggered)
mov al,B0+B4 ; ICW1: ICW1 and ICW4 flags
out 20h,al ; set ICW1
; ------------- Set MASTER control word ICW2 (base interrupt address)
mov al,IRQ_FIRST ; ICW2: base interrupt address
out 21h,al ; set ICW2
; ------------- Set MASTER control word ICW3 (cascade mask)
mov al,B2 ; ICW3: slave attached at line 2
out 21h,al ; set ICW3
; ------------- Set MASTER control word ICW4 (86 mode, nonbuffered)
mov al,B0 ; ICW4: x86 mode
or al,ah ; set EOI mode
out 21h,al ; set ICW4
; ------------- Set MASTER control word OCW2 (to read interrupt request)
mov al,B3+2 ; switch to interrupt request register
out 20h,al ; set OCW2 (IRR register)
; ------------- Set SLAVE control word ICW1 (cascade mode, level triggered)
mov al,B0+B4 ; ICW1: ICW1 and ICW4 flags
out 0a0h,al ; set ICW1
; ------------- Set SLAVE control word ICW2 (base interrupt address)
mov al,IRQ_FIRST+8 ; ICW2: base interrupt address
out 0a1h,al ; set ICW2
; ------------- Set SLAVE control word ICW3 (cascade number)
mov al,2 ; ICW3: it is slave on line 2
out 0a1h,al ; set ICW3
; ------------- Set SLAVE control word ICW4 (x86 mode, nonbuffered)
mov al,B0 ; ICW4: x86 mode
out 0a1h,al ; set ICW4
; ------------- Set SLAVE control word OCW2 (to read interrupt request)
mov al,B3+2 ; switch to interrupt request register
out 0a0h,al ; set OCW2 (IRR register)
; ------------- Delay to initialize 8259A (100 us about)
mov al,100 ; time 100 us
call UDelayByte ; short delay
; ------------- Restore current interrupt mask
mov eax,[IRQMask] ; AL <- current master mask
out 21h,al ; set master mask
mov al,ah ; AL <- current slave mask
out 0a1h,al ; set slave mask
; ------------- Acknowledge all interrupts
mov al,20h ; AL <- non-specific EOI
out 0a0h,al ; acknowledge interrupts on slave
out 20h,al ; acknowledge interrupts on master
; ------------- Unlock 8259A interrupt controller
IRQUNLOCK ; unlock 8259A interrupt controller
; ------------- Pop registers
popf ; pop flags
pop eax ; pop EAX
ret
; -----------------------------------------------------------------------------
; Initialize IRQ and 8259A controller
; -----------------------------------------------------------------------------
; NOTES: Interrupt must be disabled at this point.
; -----------------------------------------------------------------------------
; ------------- Initialize IRQ descriptor table
IRQInit: mov ebx,IRQTab ; EBX <- IRQ
xor ecx,ecx ; ECX <- 0, IRQ number
IRQInit2: call ListInit ; initialize list of IRQ handlers
mov [ebx+IRQDESC_IRQ],cl ; set IRQ number
inc ecx ; increase IRQ number
add ebx,byte IRQDESC_size ; next entry
cmp ebx,IRQTab+IRQ_NUM*IRQDESC_size ; next entry
jb IRQInit2 ; do next entry
; ------------- Initialize IRQ interrupt vectors
xor ebx,ebx ; EBX <- 0
mov bl,IRQ_FIRST ; BL <- index of first IRQ interrupt
IRQInit4: mov edx,[IRQJumpTab+ebx*4-IRQ_FIRST*4] ; EDX<-address
call SetIntGate ; set interrupt gate
cmp bl,IRQ_FIRST+IRQ_NUM ; all interrupts?
jb IRQInit4 ; next interupt
; ------------- Initialize 8259A controller
mov al,0 ; auto EOI disabled
call Init8259A ; initialize 8259A int. controller
; ------------- Install system timer handler (IRQ 0)
mov ebx,TimeIRQHandler ; EBX <- system timer handler
call IRQInstall ; install system timer handler
; ------------- Install keyboard handler (IRQ 1)
mov ebx,KeybIRQHandler ; EBX <- keyboard handler
call IRQInstall ; install keyboard handler
; ------------- Install cascade handler (IRQ 2)
mov ebx,IRQ2Handler ; EBX <- cascade handler
call IRQInstall ; install system timer handler
; ------------- Install floppy disk handler (IRQ 6)
mov ebx,FDIRQHandler ; EBX <- IRQ handler
call IRQInstall ; install IRQ handler
; ------------- Install CMOS timer handler (IRQ 8)
mov ebx,CMOSIRQHandler ; EBX <- CMOS timer handler
call IRQInstall ; install CMOS timer handler
; ------------- Install FPU handler (IRQ 13)
mov ebx,FPUIRQHandler ; EBX <- FPU handler
call IRQInstall ; install FPU handler
ret
; -----------------------------------------------------------------------------
; Data
; -----------------------------------------------------------------------------
DATA_SECTION
; ------------- 8259A interrupt controller lock
%ifdef SMP
align 4, db 0
Lock8259A: SPINLOCK ; 8259A lock
%endif
; ------------- Current mask for interrupt controllers (1=IRQ is disabled)
align 4, db 0
IRQMask: ; current mask as DWORD
IRQMask1: db 0ffh ; current mask for controller 1
IRQMask2: db 0ffh ; current mask for controller 2
dw 0 ; adjunct to DWORD
; ------------- Mask of slow handlers requiring service
align 4, db 0
IRQSlowMask: dd 0 ; mask of slow handlers requiring service
; ------------- IRQ 2 handler (cascade)
align 8, db 0
IRQ2Handler: LISTHEAD ; link to next IRQ handler
dd 0 ; pointer to IRQ descriptor
dw IRQ_PRIVATE|IRQ_ACTIVE ; IRQ flags
db 2 ; current IRQ number
db 2 ; recomended best IRQ number
dd B2 ; mask of usable IRQs (1=enabled)
dd 0 ; user data (NULL=disabled)
dd 0 ; counter for slow interrupts
dd 0 ; fast handler (NULL=none)
dd 0 ; slow handler (NULL=none)
dd 0 ; callback (NULL=none)
; -----------------------------------------------------------------------------
; Uninitialized data
; -----------------------------------------------------------------------------
BSS_SECTION
; ------------- IRQ descriptor table
align 8, resb 1
IRQTab: resb (IRQ_NUM*IRQDESC_size)
|