⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 tskkbd.asm

📁 一个多任务操作系统CTask的源代码 用C语言编写
💻 ASM
字号:
;
;	--- Version 2.2 93-06-08 10:17 ---
;
;	CTask - Keyboard handler module.
;
;	Public Domain Software written by
;		Thomas Wagner
;		Ferrari electronic Gmbh
;		Beusselstrasse 27
;		D-1000 Berlin 21
;		Germany
;
;	This module traps the keyboard interrupts to allow task switching
;	on waiting for a character.
;	To avoid problems with programs that access the keyboard buffer
;	directly instead of going through INT 16, the logic has been changed
;	in version 1.2. The keyboard characters are no longer placed into
;	a pipe, instead the keyboard hardware interrupt just sets a flag
;	to signal that there might be something in the buffer. The keyboard
;	read routines wait on this flag if the original INT 16 status call
;	indicates that no key is available.
;
;	Note that there is a slight chance of this logic leading to busy
;	waiting in the original INT 16. This could happen if the process
;	is interrupted between the status check and the actual keyboard
;	fetch, and the interrupting routine snatches away the keystroke.
;	Since this is not very likely to occur, and would not be fatal
;	anyway, it would be overkill to try to avoid this.
;
;	If INT 16 is entered via CTask's t_read_key and t_wait_key,
;	the stack is not switched.
;
;	In Version 2.1, the keyboard access routines (t_read_key etc.) 
;	were moved to file 'tsksec.asm' to save code space in secondary
;	kernels. Also, the bug that the stack was always switched, contrary
;	to what the above paragraph said, was fixed.
;	The t_xxx routines now use the extended keyboard functions
;	if available. Function 5 (stuff keyboard buffer) is intercepted
;	to set the tsk_key_avail flag.
;
;	Version 2.1 adds hotkey processing to the INT 9 frontend.
;	It would be "nicer" to process hotkeys in the INT 15 scancode
;	intercept BIOS hook, but this hook is not present in older
;	XT and AT BIOSes. Version 2.2 will use the INT 15 entry if the
;	BIOS indicates that it does support it.
;
;	CAUTION: This module can only be installed in the primary kernel.
;		 It is not ROMable.
;
	name	tskkbd
;
	include	tsk.mac
	include	tskdeb.h
;
	.tsk_model
;
	Pubfunc	tsk_install_kbd
	Pubfunc	tsk_remove_kbd
;
	public	tsk_key_avail
;
	Globext	yield
	Globext	create_flag
	Globext	delete_flag
	Globext	set_flag
	Globext	clear_flag
	Globext	wait_flag_set
	extrn	sched_int: far
	Locext	tsk_switch_stack
	Locext	tsk_old_stack
	Locext	tsk_timer_action
	Locext	tsk_dequeue
	Locext	tsk_putqueue
;
	IF	DEBUG AND DEB_FLASHERS
	Locext	tsk_inccdis
	extrn	tsk_debflash: word
	ENDIF
;
	extrn	tsk_glob_rec: byte
;
inta00		=	20h	; 8259 int controller base
eoi		=	20h	; unspecific EOI
;
keyb_data	=	60h	; keyboard data port
keyb_ctl	=	61h	; keyboard control (PC/XT only)
;
intseg	segment at 0
		org	09h*4
hwdoff		dw	?	; keyboard hardware interrupt
hwdseg		dw	?
		org	15h*4
int15off 	dw	?	; system services interrupt
int15seg 	dw	?
		org	16h*4
kbdoff		dw	?	; keyboard I/O interrupt
kbdseg		dw	?
;
intseg	ends
;
biosdata	segment at 40h
		org	17h
keyb_flags_1	db	?
keyb_flags_2	db	?
		org	96h
keyb_flags_3	db	?
;
biosdata	ends
;
;----------------------------------------------------------------------------
;
;	Variables
;
	.tsk_data
;
	IF	TSK_NAMEPAR
kbd_name	db	"KEYAVAIL",0
	ENDIF
;
tsk_key_avail	flag <>
;
	.tsk_edata
	.tsk_code
;
;
;	Original Interrupt-Entries
;
savhwd		label	dword		; original hardware int entry
savhwdoff	dw	?
savhwdseg	dw	?
;
savkbd		label	dword		; original keyboard I/O entry
savkbdoff	dw	?
savkbdseg	dw	?
;
savint15	label	dword		; original system services entry
savint15off	dw	?
savint15seg	dw	?
;
ext_keyboard	db	0		; extended keyboard BIOS present
kb_intercept	db	0		; keyboard intercept is called
sched_pending	db	0		; scheduler call is pending
;
;---------------------------------------------------------------------------
;
;	check_hotkey checks the hotkey queue passed in ES:BX for
;	a match. If there is a match, the associated action is
;	performed (via tsk_timer_action, since hotkey elements are
;	essentially timer elements).
;
;	Returns Carry set if no match, Carry clear on match.
;
	IF	HOTKEYS
;
@check_hotkey	proc	near
;
	push	ds
;
	mov	cx,SEG biosdata
	mov	ds,cx
	assume	ds:biosdata		; for checking the flags
;
	cmp	es:telem.scancode[bx],0
	je	no_scancomp
;
checkhotloop:
	cmp	es:telem.scancode[bx],al
	jne	hot_next			; no more checks on mismatch
;
no_scancomp:
	cmp	es:telem.kbflags1.hf_mask[bx],0		; check flag 1?
	je	no_flcomp1				; jump if not
;
	mov	ah,keyb_flags_1
	and	ah,es:telem.kbflags1.hf_mask[bx]
	cmp	ah,es:telem.kbflags1.hf_value[bx]
	jne	hot_next			; no more checks on mismatch
;
no_flcomp1:
	cmp	es:telem.kbflags2.hf_mask[bx],0		; check flag 2?
	je	no_flcomp2		       		; jump if not
;
	mov	ah,keyb_flags_2
	and	ah,es:telem.kbflags2.hf_mask[bx]
	cmp	ah,es:telem.kbflags2.hf_value[bx]
	jne	hot_next			; no more checks on mismatch
;
no_flcomp2:
	cmp	es:telem.kbflags3.hf_mask[bx],0		; check flag 3?
	je	do_hotkey		       		; match if not
;
	mov	ah,keyb_flags_3
	and	ah,es:telem.kbflags3.hf_mask[bx]
	cmp	ah,es:telem.kbflags3.hf_value[bx]
	je	do_hotkey			; match
;
hot_next:
	les	bx,es:tlink.q_next[bx]		; next in queue
	test	es:q_kind[bx],Q_HEAD		; queue end?
	jnz	no_hotkey
	cmp	es:telem.scancode[bx],0
	je	no_scancomp
	jmp	checkhotloop
;
no_hotkey:
	sti
	pop	ds
	assume	ds:@CTASK_DATA
	stc
	ret
;
;	Execute hotkey action. First, remove from queue to allow
;	enable/disable/delete calls in the hotkey action.
;
do_hotkey:
	pop	ds
	cli
	or	es:tflags[bx],TFLAG_BUSY	; mark busy
	push	si
	push	es
	push	bx
	callp	tsk_dequeue,<<es,bx>>		; remove
	sti
	pop	bx
	pop	es
;
	push	es
	push	bx
	callp	tsk_timer_action,<<es,bx>>
	pop	bx
	pop	es
	pop	si
;
;	If continued hotkey, re-enqueue.
;
	cli
	test	es:tflags[bx],TFLAG_UNQUEUE OR TFLAG_REMOVE
	jnz	no_enque
	test	es:tflags[bx],TFLAG_ENQUEUE OR TFLAG_REPEAT
	jz	no_enque
	push	es
	push	bx
	callp	tsk_putqueue,<<ds,si>,<es,bx>>
	pop	bx
	pop	es
;
no_enque:
	and	es:tflags[bx],NOT (TFLAG_BUSY OR TFLAG_UNQUEUE OR TFLAG_ENQUEUE)
	sti
; 	carry is now clear
	ret
;
@check_hotkey	endp
;
	ENDIF
;
;---------------------------------------------------------------------------
;
;	void tsk_install_kbd (void)
;
;		Install keyboard handler
;
Localfunc tsk_install_kbd
;
	IFDEF	LOAD_DS
	push	ds
	mov	ax,@CTASK_DATA
	mov	ds,ax
	ENDIF
;
;	Check for extended keyboard BIOS functions. Since there is no
;	error return when executing the functions in non-extended BIOS
;	versions, we have to do a little guesswork.
;
	mov	cs:ext_keyboard,0
;
	push	ds
	mov	ax,40h
	mov	ds,ax
	assume	ds:biosdata
;
	mov	ax,11ffh	; this certainly is no valid key
	int	16h		; get extended status
	cmp	ax,11ffh	; has the value changed?
	je	not_extended	; if not, it's surely not extended.
	cli
	mov	bl,keyb_flags_1
	mov	ah,12h
	int	16h
	cmp	bl,al
	jne	not_extended
	not	al
	mov	keyb_flags_1,al
	mov	ah,12h
	int	16h
	mov	keyb_flags_1,bl
	sti
	not	al
	cmp	bl,al
	jne	not_extended
;
;	An extended keyboard BIOS is present. This BIOS likely also supports
;	the INT 15 intercept hook, so check for it with the "return config
;	parameters" call.
;
	inc	cs:ext_keyboard
	IF	HOTKEYS
	mov	ah,0c0h
	int	15h
	jc	not_extended
	or	ah,ah
	jnz	not_extended
	cmp	word ptr es:[bx],8
	jb	not_extended
	test	byte ptr es:5[bx],10h	; keyboard intercept present
	jz	not_extended
	inc	cs:kb_intercept
	ENDIF
;
not_extended:
	sti
	pop	ds
;
	assume	ds:@CTASK_DATA
;
	IF	TSK_NAMEPAR
	callp	create_flag,<<ds,#tsk_key_avail>,<ds,#kbd_name>>
	ELSE
	callp	create_flag,<<ds,#tsk_key_avail>>
	ENDIF
;
;	Save old interrupt vectors
;
        push    es
	xor	ax,ax
	mov	es,ax
;
        assume  es:intseg
;
	mov	ax,kbdoff
	mov	savkbdoff,ax
	mov	ax,kbdseg
	mov	savkbdseg,ax
;
	mov	ax,hwdoff
	mov	savhwdoff,ax
	mov	ax,hwdseg
	mov	savhwdseg,ax
;
	IF	HOTKEYS
	cmp	cs:kb_intercept,0
	je	enter_new
	mov	ax,int15off
	mov	savint15off,ax
	mov	ax,int15seg
	mov	savint15seg,ax
	cli
	mov	int15off,offset @intercept
	mov	int15seg,cs
	ENDIF
;
;	Enter new Interrupt-Entries
;
enter_new:
	cli
	mov	kbdoff,offset @kbdentry
	mov	kbdseg,cs
	mov	hwdoff,offset @hwdentry
	mov	hwdseg,cs
	sti
        pop     es
;
	IFDEF	LOAD_DS
	pop	ds
	ENDIF
	ret
;
	assume	es:nothing
;
tsk_install_kbd	endp
;
;
;	void tsk_remove_kbd (void)
;
;		Un-install keyboard handler
;
Localfunc tsk_remove_kbd
;
	IFDEF	LOAD_DS
	push	ds
	mov	ax,@CTASK_DATA
	mov	ds,ax
	ENDIF
;
        push    es
	xor	ax,ax
	mov	es,ax
;
        assume  es:intseg
;
;	Restore interrupt entries
;
	cli
;
	mov	ax,savkbdoff
	mov	kbdoff,ax
	mov	ax,savkbdseg
	mov	kbdseg,ax
;
	mov	ax,savhwdoff
	mov	hwdoff,ax
	mov	ax,savhwdseg
	mov	hwdseg,ax
;
	IF	HOTKEYS
	cmp	cs:kb_intercept,0
	je	rest_ready
	mov	ax,savint15off
	mov	int15off,ax
	mov	ax,savint15seg
	mov	int15seg,ax
	ENDIF
;
rest_ready:
	sti
;
        pop     es
;
;	Delete the keyboard available flag
;
	callp	delete_flag,<<ds,#tsk_key_avail>>
;
	IFDEF	LOAD_DS
	pop	ds
	ENDIF
	ret
;
	assume	es:nothing
;
tsk_remove_kbd	endp
;
;
;
;---------------------------------------------------------------------------
;---------------------------------------------------------------------------
;
;	INT 9 - Keyboard hardware interrupt
;
;	Version 2.1 adds hotkey processing.
;
@hwdentry	proc	far
;
        call    tsk_switch_stack
	IF	DEBUG AND DEB_FLASHERS
	cmp	tsk_debflash,0
	je	debdd0
	mov	ax,DEBP_CNTKEYBD
	call	tsk_inccdis
debdd0:
	ENDIF
;
;	Check the scancode hotkey queue.
;	Two queues are maintained for hotkeys, one for hotkey elements
;	with nonzero scancode, and one for zero scancode elements.
;	If there is no scancode, the hotkey is a shift-key combination,
;	which can only be checked *after* chaining to the old INT 9.
;	Hotkeys with scancode have to be checked *before* chaining,
;	so the scancode can be removed on a match.
;	If the BIOS supports keyboard intercept, this part is skipped.
;
	IF	HOTKEYS
	cmp	cs:kb_intercept,0
	jne	no_firstcheck
	lea	si,tsk_glob_rec.hotkey_scan.q_first
	les	bx,dword ptr [si]
	test	es:q_kind[bx],Q_HEAD	; queue empty?
	jnz	no_firstcheck		; then don't read key
	in	al,keyb_data
	call	@check_hotkey
	jnc	hotkey_found		; remove scancode on match
;
no_firstcheck:
	ENDIF
;
	pushf
        cli
	call	cs:savhwd		; let original handler process key
;
	callp	set_flag,<<ds,#tsk_key_avail>>
;
;	check no-scancode hotkeys
;
	IF	HOTKEYS
	lea	si,tsk_glob_rec.hotkey_noscan.q_first
	les	bx,dword ptr [si]
	test	es:q_kind[bx],Q_HEAD	; queue empty?
	jnz	no_nshot		; don't check if yes
	call	@check_hotkey
	jnc	immed_sched		; schedule on hotkey match
;
no_nshot:
	cmp	cs:sched_pending,0
	jne	immed_sched
	ENDIF
;
	iret
;
	IF	HOTKEYS
hotkey_found:
;
;	Acknowledge keyboard, so hotkey disappears.
;
	cli
	in	al,keyb_ctl
	mov	ah,al
	or	al,80h
	out	keyb_ctl,al
	xchg	ah,al
	out	keyb_ctl,al
;
	mov	al,eoi
	out	inta00,al
	sti
;
;	on a hotkey match, we schedule immediately.
;
immed_sched:
	cli
	mov	cs:sched_pending,0
	mov	al,0bh			; access int control reg
	out	inta00,al
	in	al,inta00		; ints pending?
	or	al,al
	jnz	no_immed		; don't schedule if other ints active
	sti
	call	tsk_old_stack
	jmp	sched_int
;
no_immed:
	iret
	ENDIF
;
@hwdentry	endp
;
;
	IF	HOTKEYS
;
@intercept	proc	far
;
	cmp	ah,4fh
	je	do_intercept
	jmp	cs:savint15
;
do_intercept:
	pushf
	sti
	cld
	push	ds
	push	es
	push	si
	push	di
	push	bx
	push	ax
	mov	si,@CTASK_DATA
	mov	ds,si
;
	lea	si,tsk_glob_rec.hotkey_scan.q_first
	les	bx,dword ptr [si]
	test	es:q_kind[bx],Q_HEAD	; queue empty?
	jz	inter_check
	stc
	jmp	short inter_ret
;
inter_check:
	push	cx
	push	dx
	call	@check_hotkey
	pop	dx
	pop	cx
;
inter_ret:
	pop	ax
	pop	bx
	pop	di
	pop	si
	pop	es
	pop	ds
	jc	inter_chain
	popf
	mov	cs:sched_pending,1
	clc
	ret	2
;
inter_chain:
	popf
	jmp	cs:savint15
;
@intercept	endp
;
	ENDIF
;
;---------------------------------------------------------------------------
;---------------------------------------------------------------------------
;
;	INT 16 - Keyboard I/O
;
@kbdentry	proc	far
;
        pushf
	sti
	or	ah,ah
	jnz	kbdent1
	jmp	kbd_read
kbdent1:
	cmp	ah,1
	je	kbd_poll
	cmp	ah,11h
	je	kbd_poll
	cmp	ah,10h
	jz	kbd_read_ext	; extended read
	cmp	ah,05h
	jz	kbd_stuff	; stuff char in key buffer
	cmp	ax,4012h
	jz	kbd_readns
	cmp	ax,4112h
	jz	kbd_readns
	cmp	ax,4212h
	jz	kbd_keyhit
;
kbd_pass:
        popf
	jmp	cs:savkbd	; pass on functions != 0
;
;	The "4212" code is used by t_keyhit. It will execute function
;	1 or 11h depending on ext_keyboard.
;
kbd_keyhit:
	mov	ah,1
	cmp	cs:ext_keyboard,0
	je	kbd_pass
	mov	ah,11h
	jmp	kbd_pass
;
;	The "4012" and "4112" codes are used by t_wait_key and t_read_key.
;	The t_wait_key code 4012 uses the timeout supplied in CX:DX.
;	The t_read_key code 4112 uses an endless timeout (0L).
;
kbd_readns:
	popf			;2.1a
	sti			;2.1a
	push	ds		;2.1a
	mov	bx,@CTASK_DATA	;2.1a
	mov	ds,bx		;2.1a
	mov	bx,8001h
	cmp	cs:ext_keyboard,0
	je	readns1
	mov	bx,9011h
readns1:
;2.1a	popf
	cmp	ah,40h
	jne	kbd_read2
	jmp	short kbr_loop
;
kbd_stuff:
        popf
	call	cs:savkbd
	call	tsk_switch_stack
	mov	ax,entry_flags[bp]
	mov	caller_flags[bp],ax
	callp	set_flag,<<ds,#tsk_key_avail>>
	iret
;
kbd_read_ext:
	cmp	cs:ext_keyboard,0
	je	kbd_pass		; pass on if no extended kbd
	mov	al,11h
	jmp	short kbd_read1
;
;
kbd_poll:
	cli
	call	cs:savkbd
	jnz	kbd_poll_end
;
	push	ax
	push	bx
	push	ds
	push	es
	mov	ax,@CTASK_DATA
	mov	ds,ax
	callp	yield
	pop	es
	pop	ds
	pop	bx
	pop	ax
	cmp	ax,ax
;
kbd_poll_end:
	retf	2
;
kbd_read:
	mov	al,1
kbd_read1:
        popf
	call	tsk_switch_stack
	mov	bx,ax
kbd_read2:
	xor	cx,cx
	mov	dx,cx
;
kbr_loop:
	push	bx
	push	cx
	push	dx
	callp	clear_flag,<<ds,#tsk_key_avail>>
	pop	dx
	pop	cx
	pop	bx
	mov	ah,bl
	pushf
	cli
	call	cs:savkbd
	jnz	kbr_get_key
	push	bx
	push	cx
	push	dx
	callp	wait_flag_set,<<ds,#tsk_key_avail>,<cx,dx>>
	pop	dx
	pop	cx
	pop	bx
	or	ax,ax
	jz	kbr_loop
	mov	ax,-1
	jmp	short kbr_retn
;
kbr_get_key:
	mov	ah,bh
	and	ah,7fh
	pushf
	cli
	call	cs:savkbd
kbr_retn:
	test	bh,80h
	jnz	kbr_retns
	mov	save_ax[bp],ax
	iret			;2.1a
kbr_retns:
	pop	ds		;2.1a
	iret
;
@kbdentry	endp
;
	.tsk_ecode
	end

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -