Tvùrce webu je i pro tebe! Postav tøeba web. Bez grafika. Bez kodéra. Hned.
wz

IRQ.ASM

Interrupt Request


; =============================================================================
;
;                           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)

Back to source browser