#
# Copyright (C) 1998 BJ Eirich (aka vecna)
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
# See the GNU General Public Lic
# See the GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#

                .file   "keyboard.s"

                .extern ___djgpp_base_address
                .extern ___djgpp_ds_alias
                .extern ___djgpp_dos_sel

 # public functions and variables:

                .global _key
                .global _InitKeyboard
                .global _ShutdownKeyboard
                .global _keyboard_chain
                .global _last_pressed

                .text

                .align  4

locking_region_start:

_key:           .space  0x80, 0
_last_pressed:  .byte  0
old_vector:
old_vector_ofs: .long   0
old_vector_sel: .word   0
chain_flag:     .long   1


                .align  4

handler_procedure:

 #
 # .. will be called every time a key is pressed/released
 #
                pushl   %eax
                pushl   %edx
                pushw   %ds
 #
 # Load DS with our data selector
 #
                movw    %cs:___djgpp_ds_alias, %ds
 #
 # Read the scancode from keyboard port and update key[]
 #
                inb     $0x60, %al
                movb    %al, %dl
                andl    $0x7f, %edx
                testb   $0x80, %al
                setz    _key(%edx)
                cmp     $127, %al
                ja      nolastpressed
                movb    %edx, _last_pressed
nolastpressed:

 #
 # Chain if flag is set, otherwise do what's necessary and return
 #
                cmpl    $0, chain_flag
                jne     handler_chain
 #
 # Acknowledge keyboard and interrupt contollers
 #
                inb     $0x61, %al
                orb     $0x80, %al
                outb    %al, $0x61
                andb    $0x7f, %al
                outb    %al, $0x61
                movb    $0x20, %al
                outb    %al, $0x20

                popw    %ds
                popl    %edx
                popl    %eax
                sti
                iret

                .align  4

handler_chain:  popw    %ds
                popl    %edx
                popl    %eax
                ljmp    %cs:(old_vector)

locking_region_end:


                .align  4
_InitKeyboard:

 #
 # int keyboard_init(void);
 #
 # Initializes the keyboard handler and hooks the keyboard interrupt.
 # Returns -1 on failure, zero on success
 #
                pushl   %esi
                pushl   %edi
                pushl   %ebx
 #
 # First, we need to lock the handler and memory it touches, so
 # it doesn't get swapped out to disk.
 #
                leal    locking_region_start, %ecx
                leal    locking_region_end, %edi
                subl    %ecx, %edi
                addl    ___djgpp_base_address, %ecx
                shldl   $16, %ecx, %ebx         # ecx -> bx:cx
                shldl   $16, %edi, %esi         # edi -> si:di
                movw    $0x0600, %ax            # lock linear region
                int     $0x31
                jc      init_error
 #
 # Now we need to save the old interrupt vector, so we can restore
 # it later and also to know where to jump if chaining.
 #
                movw    $0x0204, %ax            # get pm int vector
                movb    $0x09, %bl
                int     $0x31
                movw    %cx, old_vector_sel
                movl    %edx, old_vector_ofs
 #
 # Make sure we chain after initialization.
 #
                movl    $1, chain_flag
 #
 # Set the interrupt vector to point to our handler.
 #
                movw    %cs, %cx
                leal    handler_procedure, %edx
                movb    $0x09, %bl
                movw    $0x0205, %ax            # set pm int vector
                int     $0x31
 #*
 #* Actually we would have to unlock the locked region on failure
 #* here. But since most programs would exit with an error message
 #* in such case, there's no need to worry.
 #*

init_error:

 #
 # This sets EAX to -1 if CF is set and to 0 atherwise
 #
                movl    $0, %eax
                sbbl    $0, %eax

                popl    %ebx
                popl    %edi
                popl    %esi
                ret


                .align 4
_ShutdownKeyboard:

 #
 # void keyboard_close(void);
 #
 # Shuts the keyboard handler down.
 #
                pushl   %esi
                pushl   %edi
                pushl   %ebx
 #
 # Unlock the region we locked at initialization
 #
                leal    locking_region_start, %ecx
                leal    locking_region_end, %edi
                subl    %ecx, %edi
                addl    ___djgpp_base_address, %ecx
                shldl   $16, %ecx, %ebx
                shldl   $16, %edi, %esi
                movw    $0x0601, %ax            # unlock linear region
                int     $0x31
 #
 # Restore the interrupt vector to its previous value
 #
                movw    old_vector_sel, %cx
                movl    old_vector_ofs, %edx
                movb    $0x09, %bl
                movw    $0x0205, %ax            # set pm int vector
                int     $0x31

                popl    %ebx
                popl    %edi
                popl    %esi
                ret
 #
 # void keyboard_chain(int toggle);
 #
                .align  4
_keyboard_chain:
                cmpl    $0, 4(%esp)
                je      chain_off
chain_on:

 #
 # Set the chain_flag and clear BIOS shift/ctrl/alt status bits:
 #
                movl    $1, chain_flag

                push    %es
                movw    ___djgpp_dos_sel, %es
                andb    $0xf0, %es:0x417
                pop     %es
                jmp     chain_done
chain_off:
                movl    $0, chain_flag
chain_done:     ret

