#include <stdlib.h>
#include <stdio.h>
#include <dos.h>
#include <mem.h>
#include <conio.h>
#include "types.h"
#include "keyboard.h"
#include "_keyboar.h"

/*
=============================================================================

                               GLOBAL VARIABLES

=============================================================================
*/

volatile byte KB_KeyDown[ MAXKEYBOARDSCAN ];
volatile byte KB_LastScan;


/*
=============================================================================

                                LOCAL VARIABLES

=============================================================================
*/

static boolean  KeyboardStarted = false;

static volatile boolean ExtendedKeyFlag;

static byte PauseScanCode[ NUMPAUSESCANCODES ] =
   {
   0xe1, 0x1d, 0x45, 0xe1, 0x9d, 0xc5
   };

static volatile int32 PauseCount;

static volatile char  KeyboardQueue[ KEYQMAX ];
static volatile int32 Keyhead;
static volatile int32 Keytail;

static void ( __interrupt __far *oldkeyboardisr )() = NULL;

//
// Unshifted ASCII for scan codes
//
static char ASCIINames[] =
   {
// 0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
   0  ,27 ,'1','2','3','4','5','6','7','8','9','0','-','=',8  ,9  ,  // 0
   'q','w','e','r','t','y','u','i','o','p','[',']',13 ,0  ,'a','s',  // 1
   'd','f','g','h','j','k','l',';',39 ,'`',0  ,92 ,'z','x','c','v',  // 2
   'b','n','m',',','.','/',0  ,'*',0  ,' ',0  ,0  ,0  ,0  ,0  ,0  ,  // 3
   0  ,0  ,0  ,0  ,0  ,0  ,0  ,'7','8','9','-','4','5','6','+','1',  // 4
   '2','3','0','.',0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,  // 5
   0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,  // 6
   0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0     // 7
   };

//
// Shifted ASCII for scan codes
//
static char ShiftNames[] =
   {
// 0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
   0  ,27 ,'!','@','#','$','%','^','&','*','(',')','_','+',8  ,9  ,  // 0
   'Q','W','E','R','T','Y','U','I','O','P','{','}',13 ,0  ,'A','S',  // 1
   'D','F','G','H','J','K','L',':',34 ,'~',0  ,'|','Z','X','C','V',  // 2
   'B','N','M','<','>','?',0  ,'*',0  ,' ',0  ,0  ,0  ,0  ,0  ,0  ,  // 3
   0  ,0  ,0  ,0  ,0  ,0  ,0  ,'7','8','9','-','4','5','6','+','1',  // 4
   '2','3','0','.',0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,  // 5
   0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,  // 6
   0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0     // 7
   };


/*
================
=
= KB_Isr
=
= Handles all incoming data from keyboard.
=
================
*/

static void __interrupt KB_Isr
   (
   void
   )

   {
   int32 scanin;
   int32 temp;
   int32 keyoff;
   int32 strippedkey;
   int32 nextkey;
   char  ch;

   // Get the scan code
   scanin = inp( 0x60 );

   // Tell the XT keyboard controller to clear the key
	temp = inp( 0x61 );
   outp ( 0x61, temp | 0x80 );
   outp ( 0x61, temp );

   KB_SetLastScanCode( scanin );

   // Determine if the pause key is pressed
   if ( scanin == PauseScanCode[ PauseCount ] )
      {
      PauseCount++;
      if ( PauseCount == NUMPAUSESCANCODES )
         {
         scanin = sc_Paused;
         PauseCount = 0;
         }
      }
   else
      {
      PauseCount = 0;
      }

   if ( PauseCount == 0 )
      {
      if ( scanin == 0xE0 )
         {
         ExtendedKeyFlag = true;
         }
      else
         {
         keyoff      = scanin & 0x80;
         strippedkey = scanin & 0x7f;

         //
         // When NumLock is on, the keyboard adds shift key on
         // commands to the arrow keys, so we need to remove them.
         //
         if ( ExtendedKeyFlag )
            {
            if ( ( strippedkey == sc_LShift ) ||
               ( strippedkey == sc_RShift ) )
               {
               scanin = sc_None;
               }
            }

         if ( scanin != sc_None )
            {
            //
            // Tie shift keys together
            //
            if ( strippedkey == sc_LShift )
               {
               scanin = sc_RShift | keyoff;
               strippedkey = sc_RShift;
               }

            if ( keyoff )
               {
               // Up event
               KB_KeyDown[ strippedkey ] = false;
               }
            else
               {
               // Down event
               KB_KeyDown[ strippedkey ] = true;

               // Get ascii value for key
               if ( KB_KeyPressed( sc_RShift ) )
                  {
                  ch = ShiftNames[ strippedkey ];
                  }
               else
                  {
                  ch = ASCIINames[ strippedkey ];
                  }

               //
               // if the key is not an editing key (cursor keys, insert,
               // page up, etc.) add it to the queue
               //
               if ( ( !ExtendedKeyFlag ) ||
                  ( ( ch != '.' ) && ( ( ch < '0' ) || ( ch > '9' ) ) ) )
                  {
                  nextkey = ( Keytail + 1 ) & ( KEYQMAX - 1 );
                  if ( ( ch != 0 ) && ( nextkey != Keyhead ) )
                     {
                     // Add key to keyboard queue
                     KeyboardQueue[ Keytail ] = ch;
                     Keytail = nextkey;
                     }
                  }
               }
            }

         ExtendedKeyFlag = false;
         }
      }

   // acknowledge the interrupt
   outp ( 0x20, 0x20 );
   }


/*
===================
=
= KB_KeyWaiting
=
= Checks if a character is waiting in the keyboard queue
=
===================
*/

boolean KB_KeyWaiting
   (
   void
   )

   {
   return( Keyhead != Keytail );
   }


/*
===================
=
= KB_Getch
=
= Gets the next ASCII character from the keyboard queue.  If the keyboard
= queue is empty, it waits until a key is pressed.
=
===================
*/

char KB_Getch
   (
   void
   )

   {
   char ch;

   // Wait for a key to be pressed.
   while( !KB_KeyWaiting() )
      ;

   ch = KeyboardQueue[ Keyhead ];
   Keyhead = ( Keyhead + 1 ) & ( KEYQMAX - 1 );

   return( ch );
   }


/*
===================
=
= KB_FlushKeyboardQueue
=
= Empties the keyboard queue of all waiting characters.
=
===================
*/

void KB_FlushKeyboardQueue
   (
   void
   )

   {
   Keyhead = 0;
   Keytail = 0;
   }


/*
===================
=
= KB_ClearKeysDown
=
= Clears all keys down flags.
=
===================
*/

void KB_ClearKeysDown
   (
   void
   )

   {
   KB_ClearLastScanCode();
   memset( KB_KeyDown, 0, sizeof( KB_KeyDown ) );
   }


/*
===================
=
= KB_Startup
=
===================
*/

void KB_Startup
   (
   void
   )

   {
   if ( !KeyboardStarted )
      {
      KeyboardStarted = true;

      PauseCount = 0;
      ExtendedKeyFlag = false;

      KB_FlushKeyboardQueue();
      KB_ClearKeysDown();

      oldkeyboardisr = _dos_getvect( KEYBOARDINT );
      _dos_setvect( 0x8000 | KEYBOARDINT, KB_Isr );

      printf( "KB_Startup: Keyboard Started\n" );
      }
   }


/*
===================
=
= KB_Shutdown
=
===================
*/

void KB_Shutdown
   (
   void
   )

   {
   if ( KeyboardStarted )
      {
      _dos_setvect( KEYBOARDINT, oldkeyboardisr );

      KeyboardStarted = false;

      PauseCount = 0;
      ExtendedKeyFlag = false;

      KB_FlushKeyboardQueue();
      KB_ClearKeysDown();

      // clear bios key buffer
      *( int16 * )0x41c = *( int16 * )0x41a;
      }
   }
