/*
Copywrite 1994 by Kevin P. Lawton 

This file is part of the IODEV (Input Output DEVices) component of BOCHS.

The IODEV component 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, or (at your option)
any later version.

The IODEV component 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 License for more details.

You should have received a copy of the GNU General Public License
along with the IODEV component; see the file COPYING.  If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*/


#include "iodev.h"

extern Boolean debug_on;

void enqueue_key(Bit8u scan_code, Bit8u ascii_code);
int  dequeue_key(Bit8u *scan_code, Bit8u *ascii_code, Boolean incr);


  Bit8u
bx_keyboard_io_read_handler(Bit32u address)
{
  /* 8042 keyboard controller */
bx_printf(1, "unsupported io read to keyboard port\n");
  return(0);
}

  void
bx_keyboard_io_write_handler(Bit32u address, Bit8u value)
{
  /* 8042 keyboard controller */
}

  void
bx_keyboard_int_handler(int vector)
{
}

  void
bx_init_keyboard(void)
{
  Bit8u  zero8;
  Bit16u zero16, head_offset, tail_offset;
  Bit16u start_of_buffer, end_of_buffer;
  Bit32u i;

  static char scan_codes[] = { 0x1c, 0x0d, 0x1c, 0x0d
    , 0x20, 0x64,  0x17, 0x69,  0x13, 0x72,  0x1c, 0x0d /* dir */
    , 0x2F, 0x76, 0x12, 0x65, 0x13, 0x72, 0x1c, 0x0d  /* ver */
    };


  typical_int_code[2] = 0x09;
  bx_register_int_vector(0x09, typical_int_code, sizeof(typical_int_code),
      bx_keyboard_int_handler);

  typical_int_code[2] = 0x16;
  bx_register_int_vector(0x16, typical_int_code, sizeof(typical_int_code),
      bx_int16h_handler);

  zero8  = 0;
  zero16 = 0;

  /* keyboard shift flags, set 1 */
  bx_access_physical(0x417, 1, BX_WRITE, &zero8);

  /* keyboard shift flags, set 2 */
  bx_access_physical(0x418, 1, BX_WRITE, &zero8);

  /* keyboard alt-numpad work area */
  bx_access_physical(0x419, 1, BX_WRITE, &zero8);

  /* keyboard head of buffer pointer */
  head_offset = 0x001E;
  bx_access_physical(0x41A, 2, BX_WRITE, &head_offset);

  /* keyboard end of buffer pointer */
  tail_offset = head_offset;
  bx_access_physical(0x41C, 2, BX_WRITE, &tail_offset);

  /* keyboard buffer */
  for (i=0; i<16; i++)
    bx_access_physical(0x41E + i*2, 2, BX_WRITE, &zero16);

  /* keyboard ctrl-break flag */
  bx_access_physical(0x471, 1, BX_WRITE, &zero8);

  /* keyboard pointer to start of buffer */
  start_of_buffer = 0x001E;
  bx_access_physical(0x480, 2, BX_WRITE, &start_of_buffer);

  /* keyboard pointer to end of buffer */
  end_of_buffer = 0x003E;
  bx_access_physical(0x482, 2, BX_WRITE, &end_of_buffer);

  /* keyboard status flags 3 */
  bx_access_physical(0x496, 1, BX_WRITE, &zero8);

  /* keyboard status flags 4 */
  bx_access_physical(0x497, 1, BX_WRITE, &zero8);

  for (i=0; i<sizeof(scan_codes); i += 2) {
    enqueue_key(scan_codes[i], scan_codes[i+1]);
    }
}

  void
bx_int16h_handler(int vector)
{

/* debug_on = 1; */

  switch (AH) {
    case 0x00: /* read keyboard input */
      
      if ( !dequeue_key(&AH, &AL, 1) )
        bx_printf(1, "out of input\n");
      break;

    case 0x01: /* check keyboard status */
     if ( !dequeue_key(&AH, &AL, 0) ) {
/* bx_printf(1, "out of input (check)\n"); */
        bx_cpu.eflags.zf = 1;
        return;
        }
      bx_cpu.eflags.zf = 0;
      break;

    default:
      bx_printf(1, "unsupported int 16h function %d\n", (int) AH);
    }
}

  void
enqueue_key(Bit8u scan_code, Bit8u ascii_code)
{
  Bit16u buffer_start, buffer_end, buffer_head, buffer_tail, temp_tail;

  bx_access_physical(0x480, 2, BX_READ, &buffer_start);
  bx_access_physical(0x482, 2, BX_READ, &buffer_end);

  bx_access_physical(0x41A, 2, BX_READ, &buffer_head);
  bx_access_physical(0x41C, 2, BX_READ, &buffer_tail);

  temp_tail = buffer_tail;
  buffer_tail += 2;
  if (buffer_tail >= buffer_end)
    buffer_tail = buffer_start;

  if (buffer_tail == buffer_head) {
    bx_printf(1, "dropped key scan=%02x, ascii=%02x\n",
      (int) scan_code, (int) ascii_code);
    return;
    }

  bx_access_physical(0x400 + temp_tail,   1, BX_WRITE, &ascii_code);
  bx_access_physical(0x400 + temp_tail+1, 1, BX_WRITE, &scan_code);
  bx_access_physical(0x41C, 2, BX_WRITE, &buffer_tail);
}


  int
dequeue_key(Bit8u *scan_code, Bit8u *ascii_code, Boolean incr)
{
  Bit16u buffer_start, buffer_end, buffer_head, buffer_tail;

  bx_access_physical(0x480, 2, BX_READ, &buffer_start);
  bx_access_physical(0x482, 2, BX_READ, &buffer_end);

  bx_access_physical(0x41A, 2, BX_READ, &buffer_head);
  bx_access_physical(0x41C, 2, BX_READ, &buffer_tail);

  if (buffer_head != buffer_tail) {
    bx_access_physical(0x400 + buffer_head,   1, BX_READ, ascii_code);
    bx_access_physical(0x400 + buffer_head+1, 1, BX_READ, scan_code);
    if (incr) {
      buffer_head += 2;
      if (buffer_head >= buffer_end)
        buffer_head = buffer_start;
      bx_access_physical(0x41A, 2, BX_WRITE, &buffer_head);
      }
/* if (buffer_head == 0x2a) debug_on = 1; */
    return(1);
    }
  else {
    return(0);
    }
}
