home *** CD-ROM | disk | FTP | other *** search
/ World of Shareware - Software Farm 2 / wosw_2.zip / wosw_2 / CPROG / CTASK22.ZIP / TSKKBD.ASM < prev    next >
Assembly Source File  |  1990-10-12  |  14KB  |  727 lines

  1. ;
  2. ;    --- Version 2.2 90-10-12 14:51 ---
  3. ;
  4. ;    CTask - Keyboard handler module.
  5. ;
  6. ;    Public Domain Software written by
  7. ;        Thomas Wagner
  8. ;        Ferrari electronic Gmbh
  9. ;        Beusselstrasse 27
  10. ;        D-1000 Berlin 21
  11. ;        Germany
  12. ;
  13. ;    This module traps the keyboard interrupts to allow task switching
  14. ;    on waiting for a character.
  15. ;    To avoid problems with programs that access the keyboard buffer
  16. ;    directly instead of going through INT 16, the logic has been changed
  17. ;    in version 1.2. The keyboard characters are no longer placed into
  18. ;    a pipe, instead the keyboard hardware interrupt just sets a flag
  19. ;    to signal that there might be something in the buffer. The keyboard
  20. ;    read routines wait on this flag if the original INT 16 status call
  21. ;    indicates that no key is available.
  22. ;
  23. ;    Note that there is a slight chance of this logic leading to busy
  24. ;    waiting in the original INT 16. This could happen if the process
  25. ;    is interrupted between the status check and the actual keyboard
  26. ;    fetch, and the interrupting routine snatches away the keystroke.
  27. ;    Since this is not very likely to occur, and would not be fatal
  28. ;    anyway, it would be overkill to try to avoid this.
  29. ;
  30. ;    If INT 16 is entered via CTask's t_read_key and t_wait_key,
  31. ;    the stack is not switched.
  32. ;
  33. ;    In Version 2.1, the keyboard access routines (t_read_key etc.) 
  34. ;    were moved to file 'tsksec.asm' to save code space in secondary
  35. ;    kernels. Also, the bug that the stack was always switched, contrary
  36. ;    to what the above paragraph said, was fixed.
  37. ;    The t_xxx routines now use the extended keyboard functions
  38. ;    if available. Function 5 (stuff keyboard buffer) is intercepted
  39. ;    to set the tsk_key_avail flag.
  40. ;
  41. ;    Version 2.1 adds hotkey processing to the INT 9 frontend.
  42. ;    It would be "nicer" to process hotkeys in the INT 15 scancode
  43. ;    intercept BIOS hook, but this hook is not present in older
  44. ;    XT and AT BIOSes. Version 2.2 will use the INT 15 entry if the
  45. ;    BIOS indicates that it does support it.
  46. ;
  47. ;    CAUTION: This module can only be installed in the primary kernel.
  48. ;         It is not ROMable.
  49. ;
  50.     name    tskkbd
  51. ;
  52.     include    tsk.mac
  53.     include    tskdeb.h
  54. ;
  55.     .tsk_model
  56. ;
  57.     Pubfunc    tsk_install_kbd
  58.     Pubfunc    tsk_remove_kbd
  59. ;
  60.     public    tsk_key_avail
  61. ;
  62.     Globext    create_flag
  63.     Globext    delete_flag
  64.     Globext    set_flag
  65.     Globext    clear_flag
  66.     Globext    wait_flag_set
  67.     extrn    sched_int: far
  68.     Locext    tsk_switch_stack
  69.     Locext    tsk_old_stack
  70.     Locext    tsk_timer_action
  71.     Locext    tsk_dequeue
  72.     Locext    tsk_putqueue
  73. ;
  74.     IF    DEBUG AND DEB_FLASHERS
  75.     Locext    tsk_inccdis
  76.     extrn    tsk_debflash: word
  77.     ENDIF
  78. ;
  79.     extrn    tsk_glob_rec: byte
  80. ;
  81. inta00        =    20h    ; 8259 int controller base
  82. eoi        =    20h    ; unspecific EOI
  83. ;
  84. keyb_data    =    60h    ; keyboard data port
  85. keyb_ctl    =    61h    ; keyboard control (PC/XT only)
  86. ;
  87. intseg    segment at 0
  88.         org    09h*4
  89. hwdoff        dw    ?    ; keyboard hardware interrupt
  90. hwdseg        dw    ?
  91.         org    15h*4
  92. int15off     dw    ?    ; system services interrupt
  93. int15seg     dw    ?
  94.         org    16h*4
  95. kbdoff        dw    ?    ; keyboard I/O interrupt
  96. kbdseg        dw    ?
  97. ;
  98. intseg    ends
  99. ;
  100. biosdata    segment at 40h
  101.         org    17h
  102. keyb_flags_1    db    ?
  103. keyb_flags_2    db    ?
  104.         org    96h
  105. keyb_flags_3    db    ?
  106. ;
  107. biosdata    ends
  108. ;
  109. ;----------------------------------------------------------------------------
  110. ;
  111. ;    Variables
  112. ;
  113.     .tsk_data
  114. ;
  115.     IF    TSK_NAMEPAR
  116. kbd_name    db    "KEYAVAIL",0
  117.     ENDIF
  118. ;
  119. tsk_key_avail    flag <>
  120. ;
  121.     .tsk_edata
  122.     .tsk_code
  123. ;
  124. ;
  125. ;    Original Interrupt-Entries
  126. ;
  127. savhwd        label    dword        ; original hardware int entry
  128. savhwdoff    dw    ?
  129. savhwdseg    dw    ?
  130. ;
  131. savkbd        label    dword        ; original keyboard I/O entry
  132. savkbdoff    dw    ?
  133. savkbdseg    dw    ?
  134. ;
  135. savint15    label    dword        ; original system services entry
  136. savint15off    dw    ?
  137. savint15seg    dw    ?
  138. ;
  139. ext_keyboard    db    0        ; extended keyboard BIOS present
  140. kb_intercept    db    0        ; keyboard intercept is called
  141. sched_pending    db    0        ; scheduler call is pending
  142. ;
  143. ;---------------------------------------------------------------------------
  144. ;
  145. ;    check_hotkey checks the hotkey queue passed in ES:BX for
  146. ;    a match. If there is a match, the associated action is
  147. ;    performed (via tsk_timer_action, since hotkey elements are
  148. ;    essentially timer elements).
  149. ;
  150. ;    Returns Carry set if no match, Carry clear on match.
  151. ;
  152.     IF    HOTKEYS
  153. ;
  154. @check_hotkey    proc    near
  155. ;
  156.     push    ds
  157. ;
  158.     mov    cx,SEG biosdata
  159.     mov    ds,cx
  160.     assume    ds:biosdata        ; for checking the flags
  161. ;
  162.     cmp    es:telem.scancode[bx],0
  163.     je    no_scancomp
  164. ;
  165. checkhotloop:
  166.     cmp    es:telem.scancode[bx],al
  167.     jne    hot_next            ; no more checks on mismatch
  168. ;
  169. no_scancomp:
  170.     cmp    es:telem.kbflags1.hf_mask[bx],0        ; check flag 1?
  171.     je    no_flcomp1                ; jump if not
  172. ;
  173.     mov    ah,keyb_flags_1
  174.     and    ah,es:telem.kbflags1.hf_mask[bx]
  175.     cmp    ah,es:telem.kbflags1.hf_value[bx]
  176.     jne    hot_next            ; no more checks on mismatch
  177. ;
  178. no_flcomp1:
  179.     cmp    es:telem.kbflags2.hf_mask[bx],0        ; check flag 2?
  180.     je    no_flcomp2                       ; jump if not
  181. ;
  182.     mov    ah,keyb_flags_2
  183.     and    ah,es:telem.kbflags2.hf_mask[bx]
  184.     cmp    ah,es:telem.kbflags2.hf_value[bx]
  185.     jne    hot_next            ; no more checks on mismatch
  186. ;
  187. no_flcomp2:
  188.     cmp    es:telem.kbflags3.hf_mask[bx],0        ; check flag 3?
  189.     je    do_hotkey                       ; match if not
  190. ;
  191.     mov    ah,keyb_flags_3
  192.     and    ah,es:telem.kbflags3.hf_mask[bx]
  193.     cmp    ah,es:telem.kbflags3.hf_value[bx]
  194.     je    do_hotkey            ; match
  195. ;
  196. hot_next:
  197.     les    bx,es:tlink.q_next[bx]        ; next in queue
  198.     test    es:q_kind[bx],Q_HEAD        ; queue end?
  199.     jnz    no_hotkey
  200.     cmp    es:telem.scancode[bx],0
  201.     je    no_scancomp
  202.     jmp    checkhotloop
  203. ;
  204. no_hotkey:
  205.     sti
  206.     pop    ds
  207.     assume    ds:@CTASK_DATA
  208.     stc
  209.     ret
  210. ;
  211. ;    Execute hotkey action. First, remove from queue to allow
  212. ;    enable/disable/delete calls in the hotkey action.
  213. ;
  214. do_hotkey:
  215.     pop    ds
  216.     cli
  217.     or    es:tflags[bx],TFLAG_BUSY    ; mark busy
  218.     push    si
  219.     push    es
  220.     push    bx
  221.     callp    tsk_dequeue,<<es,bx>>        ; remove
  222.     sti
  223.     pop    bx
  224.     pop    es
  225. ;
  226.     push    es
  227.     push    bx
  228.     callp    tsk_timer_action,<<es,bx>>
  229.     pop    bx
  230.     pop    es
  231.     pop    si
  232. ;
  233. ;    If continued hotkey, re-enqueue.
  234. ;
  235.     cli
  236.     test    es:tflags[bx],TFLAG_UNQUEUE OR TFLAG_REMOVE
  237.     jnz    no_enque
  238.     test    es:tflags[bx],TFLAG_ENQUEUE OR TFLAG_REPEAT
  239.     jz    no_enque
  240.     push    es
  241.     push    bx
  242.     callp    tsk_putqueue,<<ds,si>,<es,bx>>
  243.     pop    bx
  244.     pop    es
  245. ;
  246. no_enque:
  247.     and    es:tflags[bx],NOT (TFLAG_BUSY OR TFLAG_UNQUEUE OR TFLAG_ENQUEUE)
  248.     sti
  249. ;     carry is now clear
  250.     ret
  251. ;
  252. @check_hotkey    endp
  253. ;
  254.     ENDIF
  255. ;
  256. ;---------------------------------------------------------------------------
  257. ;
  258. ;    void tsk_install_kbd (void)
  259. ;
  260. ;        Install keyboard handler
  261. ;
  262. Localfunc tsk_install_kbd
  263. ;
  264.     IFDEF    LOAD_DS
  265.     push    ds
  266.     mov    ax,@CTASK_DATA
  267.     mov    ds,ax
  268.     ENDIF
  269. ;
  270. ;    Check for extended keyboard BIOS functions. Since there is no
  271. ;    error return when executing the functions in non-extended BIOS
  272. ;    versions, we have to do a little guesswork.
  273. ;
  274.     mov    cs:ext_keyboard,0
  275. ;
  276.     push    ds
  277.     mov    ax,40h
  278.     mov    ds,ax
  279.     assume    ds:biosdata
  280. ;
  281.     mov    ax,11ffh    ; this certainly is no valid key
  282.     int    16h        ; get extended status
  283.     cmp    ax,11ffh    ; has the value changed?
  284.     je    not_extended    ; if not, it's surely not extended.
  285.     cli
  286.     mov    bl,keyb_flags_1
  287.     mov    ah,12h
  288.     int    16h
  289.     cmp    bl,al
  290.     jne    not_extended
  291.     not    al
  292.     mov    keyb_flags_1,al
  293.     mov    ah,12h
  294.     int    16h
  295.     mov    keyb_flags_1,bl
  296.     sti
  297.     not    al
  298.     cmp    bl,al
  299.     jne    not_extended
  300. ;
  301. ;    An extended keyboard BIOS is present. This BIOS likely also supports
  302. ;    the INT 15 intercept hook, so check for it with the "return config
  303. ;    parameters" call.
  304. ;
  305.     inc    cs:ext_keyboard
  306.     IF    HOTKEYS
  307.     mov    ah,0c0h
  308.     int    15h
  309.     jc    not_extended
  310.     or    ah,ah
  311.     jnz    not_extended
  312.     cmp    word ptr es:[bx],8
  313.     jb    not_extended
  314.     test    byte ptr es:5[bx],10h    ; keyboard intercept present
  315.     jz    not_extended
  316.     inc    cs:kb_intercept
  317.     ENDIF
  318. ;
  319. not_extended:
  320.     sti
  321.     pop    ds
  322. ;
  323.     assume    ds:@CTASK_DATA
  324. ;
  325.     IF    TSK_NAMEPAR
  326.     callp    create_flag,<<ds,#tsk_key_avail>,<ds,#kbd_name>>
  327.     ELSE
  328.     callp    create_flag,<<ds,#tsk_key_avail>>
  329.     ENDIF
  330. ;
  331. ;    Save old interrupt vectors
  332. ;
  333.         push    es
  334.     xor    ax,ax
  335.     mov    es,ax
  336. ;
  337.         assume  es:intseg
  338. ;
  339.     mov    ax,kbdoff
  340.     mov    savkbdoff,ax
  341.     mov    ax,kbdseg
  342.     mov    savkbdseg,ax
  343. ;
  344.     mov    ax,hwdoff
  345.     mov    savhwdoff,ax
  346.     mov    ax,hwdseg
  347.     mov    savhwdseg,ax
  348. ;
  349.     IF    HOTKEYS
  350.     cmp    cs:kb_intercept,0
  351.     je    enter_new
  352.     mov    ax,int15off
  353.     mov    savint15off,ax
  354.     mov    ax,int15seg
  355.     mov    savint15seg,ax
  356.     cli
  357.     mov    int15off,offset @intercept
  358.     mov    int15seg,cs
  359.     ENDIF
  360. ;
  361. ;    Enter new Interrupt-Entries
  362. ;
  363. enter_new:
  364.     cli
  365.     mov    kbdoff,offset @kbdentry
  366.     mov    kbdseg,cs
  367.     mov    hwdoff,offset @hwdentry
  368.     mov    hwdseg,cs
  369.     sti
  370.         pop     es
  371. ;
  372.     IFDEF    LOAD_DS
  373.     pop    ds
  374.     ENDIF
  375.     ret
  376. ;
  377.     assume    es:nothing
  378. ;
  379. tsk_install_kbd    endp
  380. ;
  381. ;
  382. ;    void tsk_remove_kbd (void)
  383. ;
  384. ;        Un-install keyboard handler
  385. ;
  386. Localfunc tsk_remove_kbd
  387. ;
  388.     IFDEF    LOAD_DS
  389.     push    ds
  390.     mov    ax,@CTASK_DATA
  391.     mov    ds,ax
  392.     ENDIF
  393. ;
  394.         push    es
  395.     xor    ax,ax
  396.     mov    es,ax
  397. ;
  398.         assume  es:intseg
  399. ;
  400. ;    Restore interrupt entries
  401. ;
  402.     cli
  403. ;
  404.     mov    ax,savkbdoff
  405.     mov    kbdoff,ax
  406.     mov    ax,savkbdseg
  407.     mov    kbdseg,ax
  408. ;
  409.     mov    ax,savhwdoff
  410.     mov    hwdoff,ax
  411.     mov    ax,savhwdseg
  412.     mov    hwdseg,ax
  413. ;
  414.     IF    HOTKEYS
  415.     cmp    cs:kb_intercept,0
  416.     je    rest_ready
  417.     mov    ax,savint15off
  418.     mov    int15off,ax
  419.     mov    ax,savint15seg
  420.     mov    int15seg,ax
  421.     ENDIF
  422. ;
  423. rest_ready:
  424.     sti
  425. ;
  426.         pop     es
  427. ;
  428. ;    Delete the keyboard available flag
  429. ;
  430.     callp    delete_flag,<<ds,#tsk_key_avail>>
  431. ;
  432.     IFDEF    LOAD_DS
  433.     pop    ds
  434.     ENDIF
  435.     ret
  436. ;
  437.     assume    es:nothing
  438. ;
  439. tsk_remove_kbd    endp
  440. ;
  441. ;
  442. ;
  443. ;---------------------------------------------------------------------------
  444. ;---------------------------------------------------------------------------
  445. ;
  446. ;    INT 9 - Keyboard hardware interrupt
  447. ;
  448. ;    Version 2.1 adds hotkey processing.
  449. ;
  450. @hwdentry    proc    far
  451. ;
  452.         call    tsk_switch_stack
  453.     IF    DEBUG AND DEB_FLASHERS
  454.     cmp    tsk_debflash,0
  455.     je    debdd0
  456.     mov    ax,DEBP_CNTKEYBD
  457.     call    tsk_inccdis
  458. debdd0:
  459.     ENDIF
  460. ;
  461. ;    Check the scancode hotkey queue.
  462. ;    Two queues are maintained for hotkeys, one for hotkey elements
  463. ;    with nonzero scancode, and one for zero scancode elements.
  464. ;    If there is no scancode, the hotkey is a shift-key combination,
  465. ;    which can only be checked *after* chaining to the old INT 9.
  466. ;    Hotkeys with scancode have to be checked *before* chaining,
  467. ;    so the scancode can be removed on a match.
  468. ;    If the BIOS supports keyboard intercept, this part is skipped.
  469. ;
  470.     IF    HOTKEYS
  471.     cmp    cs:kb_intercept,0
  472.     jne    no_firstcheck
  473.     lea    si,tsk_glob_rec.hotkey_scan.q_first
  474.     les    bx,dword ptr [si]
  475.     test    es:q_kind[bx],Q_HEAD    ; queue empty?
  476.     jnz    no_firstcheck        ; then don't read key
  477.     in    al,keyb_data
  478.     call    @check_hotkey
  479.     jnc    hotkey_found        ; remove scancode on match
  480. ;
  481. no_firstcheck:
  482.     ENDIF
  483. ;
  484.     pushf
  485.         cli
  486.     call    cs:savhwd        ; let original handler process key
  487. ;
  488.     callp    set_flag,<<ds,#tsk_key_avail>>
  489. ;
  490. ;    check no-scancode hotkeys
  491. ;
  492.     IF    HOTKEYS
  493.     lea    si,tsk_glob_rec.hotkey_noscan.q_first
  494.     les    bx,dword ptr [si]
  495.     test    es:q_kind[bx],Q_HEAD    ; queue empty?
  496.     jnz    no_nshot        ; don't check if yes
  497.     call    @check_hotkey
  498.     jnc    immed_sched        ; schedule on hotkey match
  499. ;
  500. no_nshot:
  501.     cmp    cs:sched_pending,0
  502.     jne    immed_sched
  503.     ENDIF
  504. ;
  505.     iret
  506. ;
  507.     IF    HOTKEYS
  508. hotkey_found:
  509. ;
  510. ;    Acknowledge keyboard, so hotkey disappears.
  511. ;
  512.     cli
  513.     in    al,keyb_ctl
  514.     mov    ah,al
  515.     or    al,80h
  516.     out    keyb_ctl,al
  517.     xchg    ah,al
  518.     out    keyb_ctl,al
  519. ;
  520.     mov    al,eoi
  521.     out    inta00,al
  522.     sti
  523. ;
  524. ;    on a hotkey match, we schedule immediately.
  525. ;
  526. immed_sched:
  527.     cli
  528.     mov    cs:sched_pending,0
  529.     mov    al,0bh            ; access int control reg
  530.     out    inta00,al
  531.     in    al,inta00        ; ints pending?
  532.     or    al,al
  533.     jnz    no_immed        ; don't schedule if other ints active
  534.     sti
  535.     call    tsk_old_stack
  536.     jmp    sched_int
  537. ;
  538. no_immed:
  539.     iret
  540.     ENDIF
  541. ;
  542. @hwdentry    endp
  543. ;
  544. ;
  545.     IF    HOTKEYS
  546. ;
  547. @intercept    proc    far
  548. ;
  549.     cmp    ah,4fh
  550.     je    do_intercept
  551.     jmp    cs:savint15
  552. ;
  553. do_intercept:
  554.     pushf
  555.     sti
  556.     cld
  557.     push    ds
  558.     push    es
  559.     push    si
  560.     push    di
  561.     push    bx
  562.     push    ax
  563.     mov    si,@CTASK_DATA
  564.     mov    ds,si
  565. ;
  566.     lea    si,tsk_glob_rec.hotkey_scan.q_first
  567.     les    bx,dword ptr [si]
  568.     test    es:q_kind[bx],Q_HEAD    ; queue empty?
  569.     jz    inter_check
  570.     stc
  571.     jmp    short inter_ret
  572. ;
  573. inter_check:
  574.     push    cx
  575.     push    dx
  576.     call    @check_hotkey
  577.     pop    dx
  578.     pop    cx
  579. ;
  580. inter_ret:
  581.     pop    ax
  582.     pop    bx
  583.     pop    di
  584.     pop    si
  585.     pop    es
  586.     pop    ds
  587.     jc    inter_chain
  588.     popf
  589.     mov    cs:sched_pending,1
  590.     clc
  591.     ret    2
  592. ;
  593. inter_chain:
  594.     popf
  595.     jmp    cs:savint15
  596. ;
  597. @intercept    endp
  598. ;
  599.     ENDIF
  600. ;
  601. ;---------------------------------------------------------------------------
  602. ;---------------------------------------------------------------------------
  603. ;
  604. ;    INT 16 - Keyboard I/O
  605. ;
  606. @kbdentry    proc    far
  607. ;
  608.         pushf
  609.     sti
  610.     or    ah,ah
  611.     jz    kbd_read
  612.     cmp    ah,10h
  613.     jz    kbd_read_ext    ; extended read
  614.     cmp    ah,05h
  615.     jz    kbd_stuff    ; stuff char in key buffer
  616.     cmp    ax,4012h
  617.     jz    kbd_readns
  618.     cmp    ax,4112h
  619.     jz    kbd_readns
  620.     cmp    ax,4212h
  621.     jz    kbd_keyhit
  622. ;
  623. kbd_pass:
  624.         popf
  625.     jmp    cs:savkbd    ; pass on functions != 0
  626. ;
  627. ;    The "4212" code is used by t_keyhit. It will execute function
  628. ;    1 or 11h depending on ext_keyboard.
  629. ;
  630. kbd_keyhit:
  631.     mov    ah,1
  632.     cmp    cs:ext_keyboard,0
  633.     je    kbd_pass
  634.     mov    ah,11h
  635.     jmp    kbd_pass
  636. ;
  637. ;    The "4012" and "4112" codes are used by t_wait_key and t_read_key.
  638. ;    The t_wait_key code 4012 uses the timeout supplied in CX:DX.
  639. ;    The t_read_key code 4112 uses an endless timeout (0L).
  640. ;
  641. kbd_readns:
  642.     popf            ;2.1a
  643.     sti            ;2.1a
  644.     push    ds        ;2.1a
  645.     mov    bx,@CTASK_DATA    ;2.1a
  646.     mov    ds,bx        ;2.1a
  647.     mov    bx,8001h
  648.     cmp    cs:ext_keyboard,0
  649.     je    readns1
  650.     mov    bx,9011h
  651. readns1:
  652. ;2.1a    popf
  653.     cmp    ah,40h
  654.     jne    kbd_read2
  655.     jmp    short kbr_loop
  656. ;
  657. kbd_stuff:
  658.         popf
  659.     call    cs:savkbd
  660.     call    tsk_switch_stack
  661.     mov    ax,entry_flags[bp]
  662.     mov    caller_flags[bp],ax
  663.     callp    set_flag,<<ds,#tsk_key_avail>>
  664.     iret
  665. ;
  666. kbd_read_ext:
  667.     cmp    cs:ext_keyboard,0
  668.     je    kbd_pass        ; pass on if no extended kbd
  669.     mov    al,11h
  670.     jmp    short kbd_read1
  671. ;
  672. kbd_read:
  673.     mov    al,1
  674. kbd_read1:
  675.         popf
  676.     call    tsk_switch_stack
  677.     mov    bx,ax
  678. kbd_read2:
  679.     xor    cx,cx
  680.     mov    dx,cx
  681. ;
  682. kbr_loop:
  683.     push    bx
  684.     push    cx
  685.     push    dx
  686.     callp    clear_flag,<<ds,#tsk_key_avail>>
  687.     pop    dx
  688.     pop    cx
  689.     pop    bx
  690.     mov    ah,bl
  691.     pushf
  692.     cli
  693.     call    cs:savkbd
  694.     jnz    kbr_get_key
  695.     push    bx
  696.     push    cx
  697.     push    dx
  698.     callp    wait_flag_set,<<ds,#tsk_key_avail>,<cx,dx>>
  699.     pop    dx
  700.     pop    cx
  701.     pop    bx
  702.     or    ax,ax
  703.     jz    kbr_loop
  704.     mov    ax,-1
  705.     jmp    short kbr_retn
  706. ;
  707. kbr_get_key:
  708.     mov    ah,bh
  709.     and    ah,7fh
  710.     pushf
  711.     cli
  712.     call    cs:savkbd
  713. kbr_retn:
  714.     test    bh,80h
  715.     jnz    kbr_retns
  716.     mov    save_ax[bp],ax
  717.     iret            ;2.1a
  718. kbr_retns:
  719.     pop    ds        ;2.1a
  720.     iret
  721. ;
  722. @kbdentry    endp
  723. ;
  724.     .tsk_ecode
  725.     end
  726.  
  727.