; ; --- Version 2.2 90-10-12 14:51 --- ; ; 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 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,<> ; remove sti pop bx pop es ; push es push bx callp tsk_timer_action,<> 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,<,> 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,<,> ELSE callp create_flag,<> 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,<> ; 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,<> ; ; 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 jz kbd_read 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,<> 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_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,<> 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,<,> 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