diff options
Diffstat (limited to 'tools/ioemu/iodev/keyboard.cc')
-rw-r--r-- | tools/ioemu/iodev/keyboard.cc | 1611 |
1 files changed, 1611 insertions, 0 deletions
diff --git a/tools/ioemu/iodev/keyboard.cc b/tools/ioemu/iodev/keyboard.cc new file mode 100644 index 0000000000..693f4a4c60 --- /dev/null +++ b/tools/ioemu/iodev/keyboard.cc @@ -0,0 +1,1611 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: keyboard.cc,v 1.82 2003/11/11 18:18:36 vruppert Exp $ +///////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2002 MandrakeSoft S.A. +// +// MandrakeSoft S.A. +// 43, rue d'Aboukir +// 75002 Paris - France +// http://www.linux-mandrake.com/ +// http://www.mandrakesoft.com/ +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +// Now features proper implementation of keyboard opcodes 0xF4 to 0xF6 +// Silently ignores PS/2 keyboard extensions (0xF7 to 0xFD) +// Explicit panic on resend (0xFE) +// +// Emmanuel Marty <core@ggi-project.org> + +// NB: now the PS/2 mouse support is in, outb changes meaning +// in conjunction with auxb +// auxb == 0 && outb == 0 => both buffers empty (nothing to read) +// auxb == 0 && outb == 1 => keyboard controller output buffer full +// auxb == 1 && outb == 0 => not used +// auxb == 1 && outb == 1 => mouse output buffer full. +// (das) + +// Notes from Christophe Bothamy <cbbochs@free.fr> +// +// This file includes code from Ludovic Lange (http://ludovic.lange.free.fr) +// Implementation of 3 scancodes sets mf1,mf2,mf3 with or without translation. +// Default is mf2 with translation +// Ability to switch between scancodes sets +// Ability to turn translation on or off + +// Define BX_PLUGGABLE in files that can be compiled into plugins. For +// platforms that require a special tag on exported symbols, BX_PLUGGABLE +// is used to know when we are exporting symbols and when we are importing. +#define BX_PLUGGABLE + +#include "bochs.h" +#include <math.h> +#include "scancodes.h" + +#define LOG_THIS theKeyboard-> +#define VERBOSE_KBD_DEBUG 0 + + +bx_keyb_c *theKeyboard = NULL; + + int +libkeyboard_LTX_plugin_init(plugin_t *plugin, plugintype_t type, int argc, char *argv[]) +{ + // Create one instance of the keyboard device object. + theKeyboard = new bx_keyb_c (); + // Before this plugin was loaded, pluginKeyboard pointed to a stub. + // Now make it point to the real thing. + bx_devices.pluginKeyboard = theKeyboard; + // Register this device. + BX_REGISTER_DEVICE_DEVMODEL (plugin, type, theKeyboard, BX_PLUGIN_KEYBOARD); + return(0); // Success +} + + void +libkeyboard_LTX_plugin_fini(void) +{ + BX_INFO (("keyboard plugin_fini")); +} + +bx_keyb_c::bx_keyb_c(void) +{ + // constructor + put("KBD"); + settype(KBDLOG); +} + +bx_keyb_c::~bx_keyb_c(void) +{ + // destructor + BX_DEBUG(("Exit.")); +} + + +// flush internal buffer and reset keyboard settings to power-up condition + void +bx_keyb_c::resetinternals(bx_bool powerup) +{ + Bit32u i; + + BX_KEY_THIS s.kbd_internal_buffer.num_elements = 0; + for (i=0; i<BX_KBD_ELEMENTS; i++) + BX_KEY_THIS s.kbd_internal_buffer.buffer[i] = 0; + BX_KEY_THIS s.kbd_internal_buffer.head = 0; + + BX_KEY_THIS s.kbd_internal_buffer.expecting_typematic = 0; + + // Default scancode set is mf2 with translation + BX_KEY_THIS s.kbd_controller.expecting_scancodes_set = 0; + BX_KEY_THIS s.kbd_controller.current_scancodes_set = 1; + BX_KEY_THIS s.kbd_controller.scancodes_translate = 1; + + if (powerup) { + BX_KEY_THIS s.kbd_internal_buffer.expecting_led_write = 0; + BX_KEY_THIS s.kbd_internal_buffer.delay = 1; // 500 mS + BX_KEY_THIS s.kbd_internal_buffer.repeat_rate = 0x0b; // 10.9 chars/sec + } +} + + + + void +bx_keyb_c::init(void) +{ + BX_DEBUG(("Init $Id: keyboard.cc,v 1.82 2003/11/11 18:18:36 vruppert Exp $")); + Bit32u i; + + DEV_register_irq(1, "8042 Keyboard controller"); + DEV_register_irq(12, "8042 Keyboard controller (PS/2 mouse)"); + + DEV_register_ioread_handler(this, read_handler, + 0x0060, "8042 Keyboard controller", 1); + DEV_register_ioread_handler(this, read_handler, + 0x0064, "8042 Keyboard controller", 1); + DEV_register_iowrite_handler(this, write_handler, + 0x0060, "8042 Keyboard controller", 1); + DEV_register_iowrite_handler(this, write_handler, + 0x0064, "8042 Keyboard controller", 1); + BX_KEY_THIS timer_handle = bx_pc_system.register_timer( this, timer_handler, + bx_options.Okeyboard_serial_delay->get(), 1, 1, + "8042 Keyboard controller"); + + resetinternals(1); + + BX_KEY_THIS s.kbd_internal_buffer.led_status = 0; + BX_KEY_THIS s.kbd_internal_buffer.scanning_enabled = 1; + + BX_KEY_THIS s.mouse_internal_buffer.num_elements = 0; + for (i=0; i<BX_MOUSE_BUFF_SIZE; i++) + BX_KEY_THIS s.mouse_internal_buffer.buffer[i] = 0; + BX_KEY_THIS s.mouse_internal_buffer.head = 0; + + // BX_INFO(("kbd: %04d outb 0 auxb 0",__LINE__)); // das + BX_KEY_THIS s.kbd_controller.pare = 0; + BX_KEY_THIS s.kbd_controller.tim = 0; + BX_KEY_THIS s.kbd_controller.auxb = 0; + BX_KEY_THIS s.kbd_controller.keyl = 1; + BX_KEY_THIS s.kbd_controller.c_d = 1; + BX_KEY_THIS s.kbd_controller.sysf = 0; + BX_KEY_THIS s.kbd_controller.inpb = 0; + BX_KEY_THIS s.kbd_controller.outb = 0; + + BX_KEY_THIS s.kbd_controller.kbd_clock_enabled = 1; + BX_KEY_THIS s.kbd_controller.aux_clock_enabled = 0; + BX_KEY_THIS s.kbd_controller.allow_irq1 = 1; + BX_KEY_THIS s.kbd_controller.allow_irq12 = 1; + BX_KEY_THIS s.kbd_controller.kbd_output_buffer = 0; + BX_KEY_THIS s.kbd_controller.aux_output_buffer = 0; + BX_KEY_THIS s.kbd_controller.last_comm = 0; + BX_KEY_THIS s.kbd_controller.expecting_port60h = 0; + BX_KEY_THIS s.kbd_controller.irq1_requested = 0; + BX_KEY_THIS s.kbd_controller.irq12_requested = 0; + BX_KEY_THIS s.kbd_controller.expecting_mouse_parameter = 0; + +//BX_DEBUG(( "# Okeyboard_serial_delay is %u usec", +// (unsigned) bx_options.Okeyboard_serial_delay->get ())); + BX_KEY_THIS s.kbd_controller.timer_pending = 0; + + // Mouse initialization stuff + BX_KEY_THIS s.mouse.sample_rate = 100; // reports per second + BX_KEY_THIS s.mouse.resolution_cpmm = 4; // 4 counts per millimeter + BX_KEY_THIS s.mouse.scaling = 1; /* 1:1 (default) */ + BX_KEY_THIS s.mouse.mode = MOUSE_MODE_RESET; + BX_KEY_THIS s.mouse.enable = 0; + BX_KEY_THIS s.mouse.delayed_dx = 0; + BX_KEY_THIS s.mouse.delayed_dy = 0; + + for (i=0; i<BX_KBD_CONTROLLER_QSIZE; i++) + BX_KEY_THIS s.controller_Q[i] = 0; + BX_KEY_THIS s.controller_Qsize = 0; + BX_KEY_THIS s.controller_Qsource = 0; + + // clear paste buffer + BX_KEY_THIS pastebuf = NULL; + BX_KEY_THIS pastebuf_len = 0; + BX_KEY_THIS pastebuf_ptr = 0; + BX_KEY_THIS paste_delay_changed (); + BX_KEY_THIS stop_paste = 0; + + // mouse port installed on system board + DEV_cmos_set_reg(0x14, DEV_cmos_get_reg(0x14) | 0x04); + +#if BX_WITH_WX + static bx_bool first_time = 1; + if (first_time) { + first_time = 0; + // register shadow params (Experimental, not a complete list by far) + bx_list_c *list = new bx_list_c (BXP_KBD_PARAMETERS, "Keyboard State", "", 20); + list->add (new bx_shadow_bool_c (BXP_KBD_IRQ1_REQ, + "Keyboard IRQ1 requested: ", "", + &BX_KEY_THIS s.kbd_controller.irq1_requested)); + list->add (new bx_shadow_bool_c (BXP_KBD_IRQ12_REQ, + "Keyboard IRQ12 requested: ", "", + &BX_KEY_THIS s.kbd_controller.irq12_requested)); + list->add (new bx_shadow_num_c (BXP_KBD_TIMER_PENDING, + "Keyboard timer pending: ", "", + &BX_KEY_THIS s.kbd_controller.timer_pending)); + list->add (new bx_shadow_bool_c (BXP_KBD_PARE, + "Keyboard PARE", "", + &BX_KEY_THIS s.kbd_controller.pare)); + list->add (new bx_shadow_bool_c (BXP_KBD_TIM, + "Keyboard TIM", "", + &BX_KEY_THIS s.kbd_controller.tim)); + list->add (new bx_shadow_bool_c (BXP_KBD_AUXB, + "Keyboard AUXB", "", + &BX_KEY_THIS s.kbd_controller.auxb)); + list->add (new bx_shadow_bool_c (BXP_KBD_KEYL, + "Keyboard KEYL", "", + &BX_KEY_THIS s.kbd_controller.keyl)); + list->add (new bx_shadow_bool_c (BXP_KBD_C_D, + "Keyboard C_D", "", + &BX_KEY_THIS s.kbd_controller.c_d)); + list->add (new bx_shadow_bool_c (BXP_KBD_SYSF, + "Keyboard SYSF", "", + &BX_KEY_THIS s.kbd_controller.sysf)); + list->add (new bx_shadow_bool_c (BXP_KBD_INPB, + "Keyboard INPB", "", + &BX_KEY_THIS s.kbd_controller.inpb)); + list->add (new bx_shadow_bool_c (BXP_KBD_OUTB, + "Keyboard OUTB", "", + &BX_KEY_THIS s.kbd_controller.outb)); + } +#endif +} + + void +bx_keyb_c::reset(unsigned type) +{ + if (BX_KEY_THIS pastebuf != NULL) { + BX_KEY_THIS stop_paste = 1; + } +} + + void +bx_keyb_c::paste_delay_changed() +{ + BX_KEY_THIS pastedelay = bx_options.Okeyboard_paste_delay->get()/BX_IODEV_HANDLER_PERIOD; + BX_INFO(("will paste characters every %d keyboard ticks",BX_KEY_THIS pastedelay)); +} + + // static IO port read callback handler + // redirects to non-static class handler to avoid virtual functions + +// read function - the big picture: +// if address == data port then +// if byte for mouse then return it +// else if byte for keyboard then return it +// else address== status port +// assemble the status bits and return them. +// + Bit32u +bx_keyb_c::read_handler(void *this_ptr, Bit32u address, unsigned io_len) +{ +#if !BX_USE_KEY_SMF + bx_keyb_c *class_ptr = (bx_keyb_c *) this_ptr; + + return( class_ptr->read(address, io_len) ); +} + + + Bit32u +bx_keyb_c::read(Bit32u address, unsigned io_len) +{ +#else + UNUSED(this_ptr); +#endif // !BX_USE_KEY_SMF + +//BX_DEBUG(( "read from port 0x%04x", (unsigned) address)); + + if (address == 0x60) { /* output buffer */ + Bit8u val; + if (BX_KEY_THIS s.kbd_controller.auxb) { /* mouse byte available */ + val = BX_KEY_THIS s.kbd_controller.aux_output_buffer; + BX_KEY_THIS s.kbd_controller.aux_output_buffer = 0; + // BX_INFO(("kbd: %04d outb 0 auxb 0",__LINE__)); // das + BX_KEY_THIS s.kbd_controller.outb = 0; + BX_KEY_THIS s.kbd_controller.auxb = 0; + BX_KEY_THIS s.kbd_controller.irq12_requested = 0; + + if (BX_KEY_THIS s.controller_Qsize) { + unsigned i; + BX_KEY_THIS s.kbd_controller.aux_output_buffer = BX_KEY_THIS s.controller_Q[0]; + // BX_INFO(("kbd: %04d outb 1 auxb 1",__LINE__)); // das + BX_KEY_THIS s.kbd_controller.outb = 1; + BX_KEY_THIS s.kbd_controller.auxb = 1; + if (BX_KEY_THIS s.kbd_controller.allow_irq12) + BX_KEY_THIS s.kbd_controller.irq12_requested = 1; + for (i=0; i<BX_KEY_THIS s.controller_Qsize-1; i++) { + // move Q elements towards head of queue by one + BX_KEY_THIS s.controller_Q[i] = BX_KEY_THIS s.controller_Q[i+1]; + } + BX_KEY_THIS s.controller_Qsize--; + } + +//BX_DEBUG(("mouse: ___io_read aux = 0x%02x", (unsigned) val)); + + DEV_pic_lower_irq(12); + activate_timer(); + BX_DEBUG(("READ(%02x) (from mouse) = %02x", (unsigned) address, + (unsigned) val)); + return val; + } + else if (BX_KEY_THIS s.kbd_controller.outb) { /* kbd byte available */ + val = BX_KEY_THIS s.kbd_controller.kbd_output_buffer; + // BX_INFO(("kbd: %04d outb 0 auxb 0",__LINE__)); // das + BX_KEY_THIS s.kbd_controller.outb = 0; + BX_KEY_THIS s.kbd_controller.auxb = 0; + BX_KEY_THIS s.kbd_controller.irq1_requested = 0; +//BX_DEBUG(( "___io_read kbd")); + + if (BX_KEY_THIS s.controller_Qsize) { + unsigned i; + BX_KEY_THIS s.kbd_controller.aux_output_buffer = BX_KEY_THIS s.controller_Q[0]; + // BX_INFO(("kbd: %04d outb 1 auxb 1",__LINE__)); // das + BX_KEY_THIS s.kbd_controller.outb = 1; + BX_KEY_THIS s.kbd_controller.auxb = 1; + if (BX_KEY_THIS s.kbd_controller.allow_irq1) + BX_KEY_THIS s.kbd_controller.irq1_requested = 1; + for (i=0; i<BX_KEY_THIS s.controller_Qsize-1; i++) { + // move Q elements towards head of queue by one + BX_KEY_THIS s.controller_Q[i] = BX_KEY_THIS s.controller_Q[i+1]; + } + BX_DEBUG(("s.controller_Qsize: %02X",BX_KEY_THIS s.controller_Qsize)); + BX_KEY_THIS s.controller_Qsize--; + } + + DEV_pic_lower_irq(1); + activate_timer(); + BX_DEBUG(("READ(%02x) = %02x", (unsigned) address, + (unsigned) val)); + return val; + } + else { + BX_DEBUG(("num_elements = %d", BX_KEY_THIS s.kbd_internal_buffer.num_elements)); + BX_DEBUG(("read from port 60h with outb empty")); +// val = BX_KEY_THIS s.kbd_controller.kbd_output_buffer; + return BX_KEY_THIS s.kbd_controller.kbd_output_buffer; + } + } + +#if BX_CPU_LEVEL >= 2 + else if (address == 0x64) { /* status register */ + + return (BX_KEY_THIS s.kbd_controller.pare << 7) | + (BX_KEY_THIS s.kbd_controller.tim << 6) | + (BX_KEY_THIS s.kbd_controller.auxb << 5) | + (BX_KEY_THIS s.kbd_controller.keyl << 4) | + (BX_KEY_THIS s.kbd_controller.c_d << 3) | + (BX_KEY_THIS s.kbd_controller.sysf << 2) | + (BX_KEY_THIS s.kbd_controller.inpb << 1) | + BX_KEY_THIS s.kbd_controller.outb; + } + +#else /* BX_CPU_LEVEL > 0 */ + /* XT MODE, System 8255 Mode Register */ + else if (address == 0x64) { /* status register */ + BX_DEBUG(("IO read from port 64h, system 8255 mode register")); + return BX_KEY_THIS s.kbd_controller.outb; + } +#endif /* BX_CPU_LEVEL > 0 */ + + BX_PANIC(("unknown address in io read to keyboard port %x", + (unsigned) address)); + return 0; /* keep compiler happy */ +} + + + // static IO port write callback handler + // redirects to non-static class handler to avoid virtual functions + + void +bx_keyb_c::write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len) +{ +#if !BX_USE_KEY_SMF + bx_keyb_c *class_ptr = (bx_keyb_c *) this_ptr; + + class_ptr->write(address, value, io_len); +} + + void +bx_keyb_c::write( Bit32u address, Bit32u value, unsigned io_len) +{ +#else + UNUSED(this_ptr); +#endif // !BX_USE_KEY_SMF + Bit8u command_byte; + static int kbd_initialized=0; + + BX_DEBUG(("keyboard: 8-bit write to %04x = %02x", (unsigned)address, (unsigned)value)); + + switch (address) { + case 0x60: // input buffer + // if expecting data byte from command last sent to port 64h + if (BX_KEY_THIS s.kbd_controller.expecting_port60h) { + BX_KEY_THIS s.kbd_controller.expecting_port60h = 0; + // data byte written last to 0x60 + BX_KEY_THIS s.kbd_controller.c_d = 0; + if (BX_KEY_THIS s.kbd_controller.inpb) { + BX_PANIC(("write to port 60h, not ready for write")); + } + switch (BX_KEY_THIS s.kbd_controller.last_comm) { + case 0x60: // write command byte + { + bx_bool scan_convert, disable_keyboard, + disable_aux; + + scan_convert = (value >> 6) & 0x01; + disable_aux = (value >> 5) & 0x01; + disable_keyboard = (value >> 4) & 0x01; + BX_KEY_THIS s.kbd_controller.sysf = (value >> 2) & 0x01; + BX_KEY_THIS s.kbd_controller.allow_irq1 = (value >> 0) & 0x01; + BX_KEY_THIS s.kbd_controller.allow_irq12 = (value >> 1) & 0x01; + set_kbd_clock_enable(!disable_keyboard); + set_aux_clock_enable(!disable_aux); + if (BX_KEY_THIS s.kbd_controller.allow_irq12 && BX_KEY_THIS s.kbd_controller.auxb) + BX_KEY_THIS s.kbd_controller.irq12_requested = 1; + else if (BX_KEY_THIS s.kbd_controller.allow_irq1 && BX_KEY_THIS s.kbd_controller.outb) + BX_KEY_THIS s.kbd_controller.irq1_requested = 1; + + BX_DEBUG(( " allow_irq12 set to %u", (unsigned) + BX_KEY_THIS s.kbd_controller.allow_irq12)); + if ( !scan_convert ) + BX_ERROR(("keyboard: (mch) scan convert turned off")); + + // (mch) NT needs this + BX_KEY_THIS s.kbd_controller.scancodes_translate = scan_convert; + } + break; + case 0xd1: // write output port + BX_DEBUG(("write output port with value %02xh", + (unsigned) value)); + BX_SET_ENABLE_A20( (value & 0x02) != 0 ); + if (!(value & 0x01)) + BX_PANIC(("IO write: processor reset requested!")); + break; + case 0xd4: // Write to mouse + // I don't think this enables the AUX clock + //set_aux_clock_enable(1); // enable aux clock line + kbd_ctrl_to_mouse(value); + // ??? should I reset to previous value of aux enable? + break; + + case 0xd3: // write mouse output buffer + // Queue in mouse output buffer + controller_enQ(value, 1); + break; + + case 0xd2: + // Queue in keyboard output buffer + controller_enQ(value, 0); + break; + + default: + BX_PANIC(("=== unsupported write to port 60h(lastcomm=%02x): %02x", + (unsigned) BX_KEY_THIS s.kbd_controller.last_comm, (unsigned) value)); + } + } + else { + // data byte written last to 0x60 + BX_KEY_THIS s.kbd_controller.c_d = 0; + BX_KEY_THIS s.kbd_controller.expecting_port60h = 0; + /* pass byte to keyboard */ + /* ??? should conditionally pass to mouse device here ??? */ + if (BX_KEY_THIS s.kbd_controller.kbd_clock_enabled==0) { + BX_ERROR(("keyboard disabled & send of byte %02x to kbd", + (unsigned) value)); + } + kbd_ctrl_to_kbd(value); + } + break; + + case 0x64: // control register + // command byte written last to 0x64 + BX_KEY_THIS s.kbd_controller.c_d = 1; + BX_KEY_THIS s.kbd_controller.last_comm = value; + // most commands NOT expecting port60 write next + BX_KEY_THIS s.kbd_controller.expecting_port60h = 0; + + switch (value) { + case 0x20: // get keyboard command byte + BX_DEBUG(("get keyboard command byte")); + // controller output buffer must be empty + if (BX_KEY_THIS s.kbd_controller.outb) { + BX_ERROR(("kbd: OUTB set and command 0x%02x encountered", value)); + break; + } + command_byte = + (BX_KEY_THIS s.kbd_controller.scancodes_translate << 6) | + ((!BX_KEY_THIS s.kbd_controller.aux_clock_enabled) << 5) | + ((!BX_KEY_THIS s.kbd_controller.kbd_clock_enabled) << 4) | + (0 << 3) | + (BX_KEY_THIS s.kbd_controller.sysf << 2) | + (BX_KEY_THIS s.kbd_controller.allow_irq12 << 1) | + (BX_KEY_THIS s.kbd_controller.allow_irq1 << 0); + controller_enQ(command_byte, 0); + break; + case 0x60: // write command byte + BX_DEBUG(("write command byte")); + // following byte written to port 60h is command byte + BX_KEY_THIS s.kbd_controller.expecting_port60h = 1; + break; + + case 0xa0: + BX_DEBUG(("keyboard BIOS name not supported")); + break; + + case 0xa1: + BX_DEBUG(("keyboard BIOS version not supported")); + break; + + case 0xa7: // disable the aux device + set_aux_clock_enable(0); + BX_DEBUG(("aux device disabled")); + break; + case 0xa8: // enable the aux device + set_aux_clock_enable(1); + BX_DEBUG(("aux device enabled")); + break; + case 0xa9: // Test Mouse Port + // controller output buffer must be empty + if (BX_KEY_THIS s.kbd_controller.outb) { + BX_PANIC(("kbd: OUTB set and command 0x%02x encountered", value)); + break; + } + controller_enQ(0x00, 0); // no errors detected + break; + case 0xaa: // motherboard controller self test + BX_DEBUG(("Self Test")); + if( kbd_initialized == 0 ) + { + BX_KEY_THIS s.controller_Qsize = 0; + BX_KEY_THIS s.kbd_controller.outb = 0; + kbd_initialized++; + } + // controller output buffer must be empty + if (BX_KEY_THIS s.kbd_controller.outb) { + BX_ERROR(("kbd: OUTB set and command 0x%02x encountered", value)); + break; + } + // (mch) Why is this commented out??? Enabling + BX_KEY_THIS s.kbd_controller.sysf = 1; // self test complete + controller_enQ(0x55, 0); // controller OK + break; + case 0xab: // Interface Test + // controller output buffer must be empty + if (BX_KEY_THIS s.kbd_controller.outb) { +BX_PANIC(("kbd: OUTB set and command 0x%02x encountered", value)); + break; + } + controller_enQ(0x00, 0); + break; + case 0xad: // disable keyboard + set_kbd_clock_enable(0); + BX_DEBUG(("keyboard disabled")); + break; + case 0xae: // enable keyboard + set_kbd_clock_enable(1); + BX_DEBUG(("keyboard enabled")); + break; + case 0xc0: // read input port + // controller output buffer must be empty + if (BX_KEY_THIS s.kbd_controller.outb) { +BX_PANIC(("kbd: OUTB set and command 0x%02x encountered", value)); + break; + } + // keyboard power normal + controller_enQ(0x00, 0); + break; + case 0xd0: // read output port: next byte read from port 60h + BX_DEBUG(("io write to port 64h, command d0h (partial)")); + // controller output buffer must be empty + if (BX_KEY_THIS s.kbd_controller.outb) { +BX_PANIC(("kbd: OUTB set and command 0x%02x encountered", value)); + break; + } + controller_enQ( + (BX_KEY_THIS s.kbd_controller.auxb << 5) | + (BX_KEY_THIS s.kbd_controller.outb << 4) | + (BX_GET_ENABLE_A20() << 1) | + 0x01, 0); + break; + + case 0xd1: // write output port: next byte written to port 60h + BX_DEBUG(("write output port")); + // following byte to port 60h written to output port + BX_KEY_THIS s.kbd_controller.expecting_port60h = 1; + break; + + case 0xd3: // write mouse output buffer + //FIXME: Why was this a panic? + BX_DEBUG(("io write 0x64: command = 0xD3(write mouse outb)")); + // following byte to port 60h written to output port as mouse write. + BX_KEY_THIS s.kbd_controller.expecting_port60h = 1; + break; + + case 0xd4: // write to mouse + BX_DEBUG(("io write 0x64: command = 0xD4 (write to mouse)")); + // following byte written to port 60h + BX_KEY_THIS s.kbd_controller.expecting_port60h = 1; + break; + + case 0xd2: // write keyboard output buffer + BX_DEBUG(("io write 0x64: write keyboard output buffer")); + BX_KEY_THIS s.kbd_controller.expecting_port60h = 1; + break; + case 0xdd: // Disable A20 Address Line + BX_SET_ENABLE_A20(0); + break; + case 0xdf: // Enable A20 Address Line + BX_SET_ENABLE_A20(1); + break; + case 0xc1: // Continuous Input Port Poll, Low + case 0xc2: // Continuous Input Port Poll, High + case 0xe0: // Read Test Inputs + BX_PANIC(("io write 0x64: command = %02xh", (unsigned) value)); + break; + + case 0xfe: // System Reset, transition to real mode + BX_INFO(("system reset")); + bx_pc_system.ResetSignal( PCS_SET ); /* XXX is this right? */ + { + for (int i=0; i<BX_SMP_PROCESSORS; i++) + BX_CPU(i)->reset(BX_RESET_HARDWARE); + } + // Use bx_pc_system if necessary bx_cpu.reset_cpu(); + // bx_pc_system.ResetSignal( PCS_SET ); + break; + + default: + if (value==0xff || (value>=0xf0 && value<=0xfd)) { + /* useless pulse output bit commands ??? */ + BX_DEBUG(("io write to port 64h, useless command %02x", + (unsigned) value)); + return; + } + BX_PANIC(("unsupported io write to keyboard port %x, value = %x", + (unsigned) address, (unsigned) value)); + break; + } + break; + + default: BX_PANIC(("unknown address in bx_keyb_c::write()")); + } +} + +// service_paste_buf() transfers data from the paste buffer to the hardware +// keyboard buffer. It tries to transfer as many chars as possible at a +// time, but because different chars require different numbers of scancodes +// we have to be conservative. Note that this process depends on the +// keymap tables to know what chars correspond to what keys, and which +// chars require a shift or other modifier. +void +bx_keyb_c::service_paste_buf () +{ + if (!BX_KEY_THIS pastebuf) return; + BX_DEBUG (("service_paste_buf: ptr at %d out of %d", BX_KEY_THIS pastebuf_ptr, BX_KEY_THIS pastebuf_len)); + int fill_threshold = BX_KBD_ELEMENTS - 8; + while ( (BX_KEY_THIS pastebuf_ptr < BX_KEY_THIS pastebuf_len) && ! BX_KEY_THIS stop_paste) { + if (BX_KEY_THIS s.kbd_internal_buffer.num_elements >= fill_threshold) + return; + // there room in the buffer for a keypress and a key release. + // send one keypress and a key release. + Bit8u byte = BX_KEY_THIS pastebuf[BX_KEY_THIS pastebuf_ptr]; + BXKeyEntry *entry = bx_keymap.findAsciiChar (byte); + if (!entry) { + BX_ERROR (("paste character 0x%02x ignored", byte)); + } else { + BX_DEBUG (("pasting character 0x%02x. baseKey is %04x", byte, entry->baseKey)); + if (entry->modKey != BX_KEYMAP_UNKNOWN) + BX_KEY_THIS gen_scancode (entry->modKey); + BX_KEY_THIS gen_scancode (entry->baseKey); + BX_KEY_THIS gen_scancode (entry->baseKey | BX_KEY_RELEASED); + if (entry->modKey != BX_KEYMAP_UNKNOWN) + BX_KEY_THIS gen_scancode (entry->modKey | BX_KEY_RELEASED); + } + BX_KEY_THIS pastebuf_ptr++; + } + // reached end of pastebuf. free the memory it was using. + delete [] BX_KEY_THIS pastebuf; + BX_KEY_THIS pastebuf = NULL; + BX_KEY_THIS stop_paste = 0; +} + +// paste_bytes schedules an arbitrary number of ASCII characters to be +// inserted into the hardware queue as it become available. Any previous +// paste which is still in progress will be thrown out. BYTES is a pointer +// to a region of memory containing the chars to be pasted. When the paste +// is complete, the keyboard code will call delete [] bytes; +void +bx_keyb_c::paste_bytes (Bit8u *bytes, Bit32s length) +{ + BX_DEBUG (("paste_bytes: %d bytes", length)); + if (BX_KEY_THIS pastebuf) { + BX_ERROR (("previous paste was not completed! %d chars lost", + BX_KEY_THIS pastebuf_len - BX_KEY_THIS pastebuf_ptr)); + delete [] BX_KEY_THIS pastebuf; // free the old paste buffer + } + BX_KEY_THIS pastebuf = bytes; + BX_KEY_THIS pastebuf_ptr = 0; + BX_KEY_THIS pastebuf_len = length; + BX_KEY_THIS service_paste_buf (); +} + + void +bx_keyb_c::gen_scancode(Bit32u key) +{ + unsigned char *scancode; + Bit8u i; + + BX_DEBUG(( "gen_scancode(): %s %s", bx_keymap.getBXKeyName(key), (key >> 31)?"released":"pressed")); + + if (!BX_KEY_THIS s.kbd_controller.scancodes_translate) + BX_DEBUG(("keyboard: gen_scancode with scancode_translate cleared")); + + // Ignore scancode if keyboard clock is driven low + if (BX_KEY_THIS s.kbd_controller.kbd_clock_enabled==0) + return; + + // Ignore scancode if scanning is disabled + if (BX_KEY_THIS s.kbd_internal_buffer.scanning_enabled==0) + return; + + // Switch between make and break code + if (key & BX_KEY_RELEASED) + scancode=(unsigned char *)scancodes[(key&0xFF)][BX_KEY_THIS s.kbd_controller.current_scancodes_set].brek; + else + scancode=(unsigned char *)scancodes[(key&0xFF)][BX_KEY_THIS s.kbd_controller.current_scancodes_set].make; + + if (BX_KEY_THIS s.kbd_controller.scancodes_translate) { + // Translate before send + Bit8u escaped=0x00; + + for (i=0; i<strlen( (const char *)scancode ); i++) { + if (scancode[i] == 0xF0) + escaped=0x80; + else { + BX_DEBUG(("gen_scancode(): writing translated %02x",translation8042[scancode[i] ] | escaped)); + kbd_enQ(translation8042[scancode[i] ] | escaped ); + escaped=0x00; + } + } + } + else { + // Send raw data + for (i=0; i<strlen( (const char *)scancode ); i++) { + BX_DEBUG(("gen_scancode(): writing raw %02x",scancode[i])); + kbd_enQ( scancode[i] ); + } + } +} + + + + void BX_CPP_AttrRegparmN(1) +bx_keyb_c::set_kbd_clock_enable(Bit8u value) +{ + bx_bool prev_kbd_clock_enabled; + + if (value==0) { + BX_KEY_THIS s.kbd_controller.kbd_clock_enabled = 0; + } + else { + /* is another byte waiting to be sent from the keyboard ? */ + prev_kbd_clock_enabled = BX_KEY_THIS s.kbd_controller.kbd_clock_enabled; + BX_KEY_THIS s.kbd_controller.kbd_clock_enabled = 1; + + if (prev_kbd_clock_enabled==0 && BX_KEY_THIS s.kbd_controller.outb==0) { + activate_timer(); + } + } +} + + + + void +bx_keyb_c::set_aux_clock_enable(Bit8u value) +{ + bx_bool prev_aux_clock_enabled; + + BX_DEBUG(("set_aux_clock_enable(%u)", (unsigned) value)); + if (value==0) { + BX_KEY_THIS s.kbd_controller.aux_clock_enabled = 0; + } + else { + /* is another byte waiting to be sent from the keyboard ? */ + prev_aux_clock_enabled = BX_KEY_THIS s.kbd_controller.aux_clock_enabled; + BX_KEY_THIS s.kbd_controller.aux_clock_enabled = 1; + if (prev_aux_clock_enabled==0 && BX_KEY_THIS s.kbd_controller.outb==0) + activate_timer(); + } +} + + Bit8u +bx_keyb_c::get_kbd_enable(void) +{ + BX_DEBUG(("get_kbd_enable(): getting kbd_clock_enabled of: %02x", + (unsigned) BX_KEY_THIS s.kbd_controller.kbd_clock_enabled)); + + return(BX_KEY_THIS s.kbd_controller.kbd_clock_enabled); +} + + void +bx_keyb_c::controller_enQ(Bit8u data, unsigned source) +{ + // source is 0 for keyboard, 1 for mouse + + BX_DEBUG(("controller_enQ(%02x) source=%02x", (unsigned) data,source)); + + if (BX_KEY_THIS s.kbd_controller.outb) + BX_ERROR(("controller_enQ(): OUTB set!")); + + // see if we need to Q this byte from the controller + // remember this includes mouse bytes. + if (BX_KEY_THIS s.kbd_controller.outb) { + if (BX_KEY_THIS s.controller_Qsize >= BX_KBD_CONTROLLER_QSIZE) + BX_PANIC(("controller_enq(): controller_Q full!")); + BX_KEY_THIS s.controller_Q[BX_KEY_THIS s.controller_Qsize++] = data; + BX_KEY_THIS s.controller_Qsource = source; + return; + } + + // the Q is empty + if (source == 0) { // keyboard + BX_KEY_THIS s.kbd_controller.kbd_output_buffer = data; + // BX_INFO(("kbd: %04d outb 1 auxb 0",__LINE__)); // das + BX_KEY_THIS s.kbd_controller.outb = 1; + BX_KEY_THIS s.kbd_controller.auxb = 0; + BX_KEY_THIS s.kbd_controller.inpb = 0; + if (BX_KEY_THIS s.kbd_controller.allow_irq1) + BX_KEY_THIS s.kbd_controller.irq1_requested = 1; + } + else { // mouse + BX_KEY_THIS s.kbd_controller.aux_output_buffer = data; + // BX_INFO(("kbd: %04d outb 1 auxb 1",__LINE__)); // das + BX_KEY_THIS s.kbd_controller.outb = 1; + BX_KEY_THIS s.kbd_controller.auxb = 1; + BX_KEY_THIS s.kbd_controller.inpb = 0; + if (BX_KEY_THIS s.kbd_controller.allow_irq12) + BX_KEY_THIS s.kbd_controller.irq12_requested = 1; + } +} + +void +bx_keyb_c::kbd_enQ_imm(Bit8u val) +{ + int tail; + + if (BX_KEY_THIS s.kbd_internal_buffer.num_elements >= BX_KBD_ELEMENTS) { + BX_PANIC(("internal keyboard buffer full (imm)")); + return; + } + + /* enqueue scancode in multibyte internal keyboard buffer */ + tail = (BX_KEY_THIS s.kbd_internal_buffer.head + BX_KEY_THIS s.kbd_internal_buffer.num_elements) % + BX_KBD_ELEMENTS; + + BX_KEY_THIS s.kbd_controller.kbd_output_buffer = val; + // BX_INFO(("kbd: %04d outb 1",__LINE__)); // das + BX_KEY_THIS s.kbd_controller.outb = 1; + + if (BX_KEY_THIS s.kbd_controller.allow_irq1) + BX_KEY_THIS s.kbd_controller.irq1_requested = 1; +} + + + void +bx_keyb_c::kbd_enQ(Bit8u scancode) +{ + int tail; + + BX_DEBUG(("kbd_enQ(0x%02x)", (unsigned) scancode)); + + if (BX_KEY_THIS s.kbd_internal_buffer.num_elements >= BX_KBD_ELEMENTS) { + BX_INFO(("internal keyboard buffer full, ignoring scancode.(%02x)", + (unsigned) scancode)); + return; + } + + /* enqueue scancode in multibyte internal keyboard buffer */ + BX_DEBUG(("kbd_enQ: putting scancode 0x%02x in internal buffer", + (unsigned) scancode)); + tail = (BX_KEY_THIS s.kbd_internal_buffer.head + BX_KEY_THIS s.kbd_internal_buffer.num_elements) % + BX_KBD_ELEMENTS; + BX_KEY_THIS s.kbd_internal_buffer.buffer[tail] = scancode; + BX_KEY_THIS s.kbd_internal_buffer.num_elements++; + + if (!BX_KEY_THIS s.kbd_controller.outb && BX_KEY_THIS s.kbd_controller.kbd_clock_enabled) { + activate_timer(); + BX_DEBUG(("activating timer...")); + return; + } +//BX_DEBUG(( "# not activating timer..."); +//BX_DEBUG(( "# allow_irq1 = %u", (unsigned) BX_KEY_THIS s.kbd_controller.allow_irq1); +//BX_DEBUG(( "# outb = %u", (unsigned) BX_KEY_THIS s.kbd_controller.outb); +//BX_DEBUG(( "# clock_enab = %u", (unsigned) BX_KEY_THIS s.kbd_controller.kbd_clock_enabled); +//BX_DEBUG(( "# out_buffer = %u", (unsigned) BX_KEY_THIS s.kbd_controller.kbd_output_buffer); +} + + bx_bool BX_CPP_AttrRegparmN(3) +bx_keyb_c::mouse_enQ_packet(Bit8u b1, Bit8u b2, Bit8u b3) +{ + if ((BX_KEY_THIS s.mouse_internal_buffer.num_elements + 3) >= BX_MOUSE_BUFF_SIZE) { + return(0); /* buffer doesn't have the space */ + } + +//BX_DEBUG(("mouse: enQ_packet(%02x, %02x, %02x)", +// (unsigned) b1, (unsigned) b2, (unsigned) b3)); + + mouse_enQ(b1); + mouse_enQ(b2); + mouse_enQ(b3); + return(1); +} + + + void +bx_keyb_c::mouse_enQ(Bit8u mouse_data) +{ + int tail; + + BX_DEBUG(("mouse_enQ(%02x)", (unsigned) mouse_data)); + + if (BX_KEY_THIS s.mouse_internal_buffer.num_elements >= BX_MOUSE_BUFF_SIZE) { + BX_ERROR(("mouse: internal mouse buffer full, ignoring mouse data.(%02x)", + (unsigned) mouse_data)); + return; + } +//BX_DEBUG(( "# mouse_enq() aux_clock_enabled = %u", +// (unsigned) BX_KEY_THIS s.kbd_controller.aux_clock_enabled); + + /* enqueue mouse data in multibyte internal mouse buffer */ + tail = (BX_KEY_THIS s.mouse_internal_buffer.head + BX_KEY_THIS s.mouse_internal_buffer.num_elements) % + BX_MOUSE_BUFF_SIZE; + BX_KEY_THIS s.mouse_internal_buffer.buffer[tail] = mouse_data; + BX_KEY_THIS s.mouse_internal_buffer.num_elements++; + + if (!BX_KEY_THIS s.kbd_controller.outb && BX_KEY_THIS s.kbd_controller.aux_clock_enabled) { + activate_timer(); +//BX_DEBUG(( "# activating timer..."); + return; + } +//BX_DEBUG(( "# not activating timer..."); +//BX_DEBUG(( "# allow_irq12= %u", (unsigned) BX_KEY_THIS s.kbd_controller.allow_irq12); +//BX_DEBUG(( "# outb = %u", (unsigned) BX_KEY_THIS s.kbd_controller.outb); +//BX_DEBUG(( "# clock_enab = %u", (unsigned) BX_KEY_THIS s.kbd_controller.aux_clock_enabled); +//BX_DEBUG(( "# out_buffer = %u", (unsigned) BX_KEY_THIS s.kbd_controller.aux_output_buffer); +} + + void +bx_keyb_c::kbd_ctrl_to_kbd(Bit8u value) +{ + + BX_DEBUG(("controller passed byte %02xh to keyboard", value)); + + if (BX_KEY_THIS s.kbd_internal_buffer.expecting_typematic) { + BX_KEY_THIS s.kbd_internal_buffer.expecting_typematic = 0; + BX_KEY_THIS s.kbd_internal_buffer.delay = (value >> 5) & 0x03; + switch (BX_KEY_THIS s.kbd_internal_buffer.delay) { + case 0: BX_INFO(("setting delay to 250 mS (unused)")); break; + case 1: BX_INFO(("setting delay to 500 mS (unused)")); break; + case 2: BX_INFO(("setting delay to 750 mS (unused)")); break; + case 3: BX_INFO(("setting delay to 1000 mS (unused)")); break; + } + BX_KEY_THIS s.kbd_internal_buffer.repeat_rate = value & 0x1f; + double cps = 1 /((double)(8 + (value & 0x07)) * (double)exp(log((double)2) * (double)((value >> 3) & 0x03)) * 0.00417); + BX_INFO(("setting repeat rate to %.1f cps (unused)", cps)); + kbd_enQ(0xFA); // send ACK + return; + } + + if (BX_KEY_THIS s.kbd_internal_buffer.expecting_led_write) { + BX_KEY_THIS s.kbd_internal_buffer.expecting_led_write = 0; + BX_KEY_THIS s.kbd_internal_buffer.led_status = value; + BX_DEBUG(("LED status set to %02x", + (unsigned) BX_KEY_THIS s.kbd_internal_buffer.led_status)); + kbd_enQ(0xFA); // send ACK %%% + return; + } + + if (BX_KEY_THIS s.kbd_controller.expecting_scancodes_set) { + BX_KEY_THIS s.kbd_controller.expecting_scancodes_set = 0; + if( value != 0 ) { + if( value<4 ) { + BX_KEY_THIS s.kbd_controller.current_scancodes_set = (value-1); + BX_INFO(("Switched to scancode set %d\n", + (unsigned) BX_KEY_THIS s.kbd_controller.current_scancodes_set + 1)); + kbd_enQ(0xFA); + } + else { + BX_ERROR(("Received scancodes set out of range: %d\n", value )); + kbd_enQ(0xFF); // send ERROR + } + } + else { + // Send current scancodes set to port 0x60 + kbd_enQ( 1 + (BX_KEY_THIS s.kbd_controller.current_scancodes_set) ); + } + return; + } + + switch (value) { + case 0x00: // ??? ignore and let OS timeout with no response + kbd_enQ(0xFA); // send ACK %%% + return; + break; + + case 0x05: // ??? + // (mch) trying to get this to work... + BX_KEY_THIS s.kbd_controller.sysf = 1; + kbd_enQ_imm(0xfe); + return; + break; + + case 0xed: // LED Write + BX_KEY_THIS s.kbd_internal_buffer.expecting_led_write = 1; + kbd_enQ_imm(0xFA); // send ACK %%% + return; + break; + + case 0xee: // echo + kbd_enQ(0xEE); // return same byte (EEh) as echo diagnostic + return; + break; + + case 0xf0: // Select alternate scan code set + BX_KEY_THIS s.kbd_controller.expecting_scancodes_set = 1; + BX_DEBUG(("Expecting scancode set info...\n")); + kbd_enQ(0xFA); // send ACK + return; + break; + + case 0xf2: // identify keyboard + BX_INFO(("identify keyboard command received")); + + // XT sends nothing, AT sends ACK + // MFII with translation sends ACK+ABh+41h + // MFII without translation sends ACK+ABh+83h + if (bx_options.Okeyboard_type->get() != BX_KBD_XT_TYPE) { + kbd_enQ(0xFA); + if (bx_options.Okeyboard_type->get() == BX_KBD_MF_TYPE) { + kbd_enQ(0xAB); + + if(BX_KEY_THIS s.kbd_controller.scancodes_translate) + kbd_enQ(0x41); + else + kbd_enQ(0x83); + } + } + return; + break; + + case 0xf3: // typematic info + BX_KEY_THIS s.kbd_internal_buffer.expecting_typematic = 1; + BX_INFO(("setting typematic info")); + kbd_enQ(0xFA); // send ACK + return; + break; + + case 0xf4: // enable keyboard + BX_KEY_THIS s.kbd_internal_buffer.scanning_enabled = 1; + kbd_enQ(0xFA); // send ACK + return; + break; + + case 0xf5: // reset keyboard to power-up settings and disable scanning + resetinternals(1); + kbd_enQ(0xFA); // send ACK + BX_KEY_THIS s.kbd_internal_buffer.scanning_enabled = 0; + BX_INFO(("reset-disable command received")); + return; + break; + + case 0xf6: // reset keyboard to power-up settings and enable scanning + resetinternals(1); + kbd_enQ(0xFA); // send ACK + BX_KEY_THIS s.kbd_internal_buffer.scanning_enabled = 1; + BX_INFO(("reset-enable command received")); + return; + break; + + case 0xf7: // PS/2 Set All Keys To Typematic + case 0xf8: // PS/2 Set All Keys to Make/Break + case 0xf9: // PS/2 PS/2 Set All Keys to Make + case 0xfa: // PS/2 Set All Keys to Typematic Make/Break + case 0xfb: // PS/2 Set Key Type to Typematic + case 0xfc: // PS/2 Set Key Type to Make/Break + case 0xfd: // PS/2 Set Key Type to Make + // Silently ignore and let the OS timeout, for now. + // If anyone has code around that makes use of that, I can + // provide documentation on their behavior (ask core@ggi-project.org) + return; + break; + + case 0xfe: // resend. aiiee. + BX_PANIC( ("got 0xFE (resend)")); + return; + break; + + case 0xff: // reset: internal keyboard reset and afterwards the BAT + BX_DEBUG(("reset command received")); + resetinternals(1); + kbd_enQ(0xFA); // send ACK + kbd_enQ(0xAA); // BAT test passed + return; + break; + + case 0xd3: + kbd_enQ(0xfa); + return; + + default: + /* XXX fix this properly: + http://panda.cs.ndsu.nodak.edu/~achapwes/PICmicro/mouse/mouse.html + http://sourceforge.net/tracker/index.php?func=detail&aid=422457&group_id=12580&atid=112580 + */ + BX_ERROR(("kbd_ctrl_to_kbd(): got value of %02x", + (unsigned) value)); + kbd_enQ(0xFA); /* send ACK ??? */ + return; + break; + } +} + + void +bx_keyb_c::timer_handler(void *this_ptr) +{ + bx_keyb_c *class_ptr = (bx_keyb_c *) this_ptr; + unsigned retval; + + // retval=class_ptr->periodic( bx_options.Okeyboard_serial_delay->get()); + retval=class_ptr->periodic(1); + + if(retval&0x01) + DEV_pic_raise_irq(1); + if(retval&0x02) + DEV_pic_raise_irq(12); +} + + unsigned +bx_keyb_c::periodic( Bit32u usec_delta ) +{ +/* static int multiple=0; */ + static unsigned count_before_paste=0; + Bit8u retval; + + UNUSED( usec_delta ); + + if (BX_KEY_THIS s.kbd_controller.kbd_clock_enabled ) { + if(++count_before_paste>=BX_KEY_THIS pastedelay) { + // after the paste delay, consider adding moving more chars + // from the paste buffer to the keyboard buffer. + BX_KEY_THIS service_paste_buf (); + count_before_paste=0; + } + } + + retval = BX_KEY_THIS s.kbd_controller.irq1_requested | (BX_KEY_THIS s.kbd_controller.irq12_requested << 1); + BX_KEY_THIS s.kbd_controller.irq1_requested = 0; + BX_KEY_THIS s.kbd_controller.irq12_requested = 0; + + if ( BX_KEY_THIS s.kbd_controller.timer_pending == 0 ) { + return(retval); + } + + if ( usec_delta >= BX_KEY_THIS s.kbd_controller.timer_pending ) { + BX_KEY_THIS s.kbd_controller.timer_pending = 0; + } + else { + BX_KEY_THIS s.kbd_controller.timer_pending -= usec_delta; + return(retval); + } + + if (BX_KEY_THIS s.kbd_controller.outb) { + return(retval); + } + + /* nothing in outb, look for possible data xfer from keyboard or mouse */ + if (BX_KEY_THIS s.kbd_controller.kbd_clock_enabled && BX_KEY_THIS s.kbd_internal_buffer.num_elements) { +//BX_DEBUG(( "# servicing keyboard code"); + BX_DEBUG(("service_keyboard: key in internal buffer waiting")); + BX_KEY_THIS s.kbd_controller.kbd_output_buffer = + BX_KEY_THIS s.kbd_internal_buffer.buffer[BX_KEY_THIS s.kbd_internal_buffer.head]; + // BX_INFO(("kbd: %04d outb 1",__LINE__)); // das + BX_KEY_THIS s.kbd_controller.outb = 1; + // commented out since this would override the current state of the + // mouse buffer flag - no bug seen - just seems wrong (das) + // BX_KEY_THIS s.kbd_controller.auxb = 0; +//BX_DEBUG(( "# ___kbd::periodic kbd"); + BX_KEY_THIS s.kbd_internal_buffer.head = (BX_KEY_THIS s.kbd_internal_buffer.head + 1) % + BX_KBD_ELEMENTS; + BX_KEY_THIS s.kbd_internal_buffer.num_elements--; + if (BX_KEY_THIS s.kbd_controller.allow_irq1) + BX_KEY_THIS s.kbd_controller.irq1_requested = 1; + } + else { + create_mouse_packet(0); + if (BX_KEY_THIS s.kbd_controller.aux_clock_enabled && BX_KEY_THIS s.mouse_internal_buffer.num_elements) { +//BX_DEBUG(( "# servicing mouse code"); + BX_DEBUG(("service_keyboard: key(from mouse) in internal buffer waiting")); + BX_KEY_THIS s.kbd_controller.aux_output_buffer = + BX_KEY_THIS s.mouse_internal_buffer.buffer[BX_KEY_THIS s.mouse_internal_buffer.head]; + + // BX_INFO(("kbd: %04d outb 1 auxb 1",__LINE__)); //das + BX_KEY_THIS s.kbd_controller.outb = 1; + BX_KEY_THIS s.kbd_controller.auxb = 1; +//BX_DEBUG(( "# ___kbd:periodic aux"); + BX_KEY_THIS s.mouse_internal_buffer.head = (BX_KEY_THIS s.mouse_internal_buffer.head + 1) % + BX_MOUSE_BUFF_SIZE; + BX_KEY_THIS s.mouse_internal_buffer.num_elements--; +//BX_DEBUG(( "# allow12 = %u", (unsigned) BX_KEY_THIS s.kbd_controller.allow_irq12); + if (BX_KEY_THIS s.kbd_controller.allow_irq12) + BX_KEY_THIS s.kbd_controller.irq12_requested = 1; + } + else { + BX_DEBUG(("service_keyboard(): no keys waiting")); + } + } + return(retval); +} + + + + + void +bx_keyb_c::activate_timer(void) +{ + if (BX_KEY_THIS s.kbd_controller.timer_pending == 0) { + // BX_KEY_THIS s.kbd_controller.timer_pending = bx_options.Okeyboard_serial_delay->get (); + BX_KEY_THIS s.kbd_controller.timer_pending = 1; + } +} + + + void +bx_keyb_c::kbd_ctrl_to_mouse(Bit8u value) +{ +BX_DEBUG(("MOUSE: kbd_ctrl_to_mouse(%02xh)", (unsigned) value)); +BX_DEBUG((" enable = %u", (unsigned) BX_KEY_THIS s.mouse.enable)); +BX_DEBUG((" allow_irq12 = %u", + (unsigned) BX_KEY_THIS s.kbd_controller.allow_irq12)); +BX_DEBUG((" aux_clock_enabled = %u", + (unsigned) BX_KEY_THIS s.kbd_controller.aux_clock_enabled)); +//BX_DEBUG(( "MOUSE: kbd_ctrl_to_mouse(%02xh)", (unsigned) value)); + + // an ACK (0xFA) is always the first response to any valid input + // received from the system other than Set-Wrap-Mode & Resend-Command + + + if (BX_KEY_THIS s.kbd_controller.expecting_mouse_parameter) { + BX_KEY_THIS s.kbd_controller.expecting_mouse_parameter = 0; + switch (BX_KEY_THIS s.kbd_controller.last_mouse_command) { + case 0xf3: // Set Mouse Sample Rate + BX_KEY_THIS s.mouse.sample_rate = value; + BX_DEBUG(("[mouse] Sampling rate set: %d Hz", value)); + controller_enQ(0xFA, 1); // ack + break; + + case 0xe8: // Set Mouse Resolution + switch (value) { + case 0: + BX_KEY_THIS s.mouse.resolution_cpmm = 1; + break; + case 1: + BX_KEY_THIS s.mouse.resolution_cpmm = 2; + break; + case 2: + BX_KEY_THIS s.mouse.resolution_cpmm = 4; + break; + case 3: + BX_KEY_THIS s.mouse.resolution_cpmm = 8; + break; + default: + BX_PANIC(("[mouse] Unknown resolution %d", value)); + break; + } + BX_DEBUG(("[mouse] Resolution set to %d counts per mm", + BX_KEY_THIS s.mouse.resolution_cpmm)); + + controller_enQ(0xFA, 1); // ack + break; + + default: + BX_PANIC(("MOUSE: unknown last command (%02xh)", (unsigned) BX_KEY_THIS s.kbd_controller.last_mouse_command)); + } + } else { + BX_KEY_THIS s.kbd_controller.expecting_mouse_parameter = 0; + BX_KEY_THIS s.kbd_controller.last_mouse_command = value; + + // test for wrap mode first + if (BX_KEY_THIS s.mouse.mode == MOUSE_MODE_WRAP) { + // if not a reset command or reset wrap mode + // then just echo the byte. + if ((value != 0xff) && (value != 0xec)) { + if (bx_dbg.mouse) + BX_INFO(("[mouse] wrap mode: Ignoring command %0X02.",value)); + controller_enQ(value,1); + // bail out + return; + } + } + switch ( value ) { + case 0xe6: // Set Mouse Scaling to 1:1 + controller_enQ(0xFA, 1); // ACK + BX_KEY_THIS s.mouse.scaling = 2; + BX_DEBUG(("[mouse] Scaling set to 1:1")); + break; + + case 0xe7: // Set Mouse Scaling to 2:1 + controller_enQ(0xFA, 1); // ACK + BX_KEY_THIS s.mouse.scaling = 2; + BX_DEBUG(("[mouse] Scaling set to 2:1")); + break; + + case 0xe8: // Set Mouse Resolution + controller_enQ(0xFA, 1); // ACK + BX_KEY_THIS s.kbd_controller.expecting_mouse_parameter = 1; + break; + + case 0xea: // Set Stream Mode + if (bx_dbg.mouse) + BX_INFO(("[mouse] Mouse stream mode on.")); + BX_KEY_THIS s.mouse.mode = MOUSE_MODE_STREAM; + controller_enQ(0xFA, 1); // ACK + break; + + case 0xec: // Reset Wrap Mode + // unless we are in wrap mode ignore the command + if ( BX_KEY_THIS s.mouse.mode == MOUSE_MODE_WRAP) { + if (bx_dbg.mouse) + BX_INFO(("[mouse] Mouse wrap mode off.")); + // restore previous mode except disable stream mode reporting. + // ### TODO disabling reporting in stream mode + BX_KEY_THIS s.mouse.mode = BX_KEY_THIS s.mouse.saved_mode; + controller_enQ(0xFA, 1); // ACK + } + break; + case 0xee: // Set Wrap Mode + // ### TODO flush output queue. + // ### TODO disable interrupts if in stream mode. + if (bx_dbg.mouse) + BX_INFO(("[mouse] Mouse wrap mode on.")); + BX_KEY_THIS s.mouse.saved_mode = BX_KEY_THIS s.mouse.mode; + BX_KEY_THIS s.mouse.mode = MOUSE_MODE_WRAP; + controller_enQ(0xFA, 1); // ACK + break; + + case 0xf0: // Set Remote Mode (polling mode, i.e. not stream mode.) + if (bx_dbg.mouse) + BX_INFO(("[mouse] Mouse remote mode on.")); + // ### TODO should we flush/discard/ignore any already queued packets? + BX_KEY_THIS s.mouse.mode = MOUSE_MODE_REMOTE; + controller_enQ(0xFA, 1); // ACK + break; + + + case 0xf2: // Read Device Type + controller_enQ(0xFA, 1); // ACK + controller_enQ(0x00, 1); // Device ID + BX_DEBUG(("[mouse] Read mouse ID")); + break; + + case 0xf3: // Set Mouse Sample Rate (sample rate written to port 60h) + controller_enQ(0xFA, 1); // ACK + BX_KEY_THIS s.kbd_controller.expecting_mouse_parameter = 1; + break; + + case 0xf4: // Enable (in stream mode) + BX_KEY_THIS s.mouse.enable = 1; + controller_enQ(0xFA, 1); // ACK + BX_DEBUG(("[mouse] Mouse enabled (stream mode)")); + break; + + case 0xf5: // Disable (in stream mode) + BX_KEY_THIS s.mouse.enable = 0; + controller_enQ(0xFA, 1); // ACK + BX_DEBUG(("[mouse] Mouse disabled (stream mode)")); + break; + + case 0xf6: // Set Defaults + BX_KEY_THIS s.mouse.sample_rate = 100; /* reports per second (default) */ + BX_KEY_THIS s.mouse.resolution_cpmm = 4; /* 4 counts per millimeter (default) */ + BX_KEY_THIS s.mouse.scaling = 1; /* 1:1 (default) */ + BX_KEY_THIS s.mouse.enable = 0; + BX_KEY_THIS s.mouse.mode = MOUSE_MODE_STREAM; + controller_enQ(0xFA, 1); // ACK + BX_DEBUG(("[mouse] Set Defaults")); + break; + + case 0xff: // Reset + BX_KEY_THIS s.mouse.sample_rate = 100; /* reports per second (default) */ + BX_KEY_THIS s.mouse.resolution_cpmm = 4; /* 4 counts per millimeter (default) */ + BX_KEY_THIS s.mouse.scaling = 1; /* 1:1 (default) */ + BX_KEY_THIS s.mouse.mode = MOUSE_MODE_RESET; + BX_KEY_THIS s.mouse.enable = 0; + /* (mch) NT expects an ack here */ + controller_enQ(0xFA, 1); // ACK + controller_enQ(0xAA, 1); // completion code + controller_enQ(0x00, 1); // ID code (normal mouse, wheelmouse has id 0x3) + BX_DEBUG(("[mouse] Mouse reset")); + break; + + case 0xe9: // Get mouse information + // should we ack here? (mch): Yes + controller_enQ(0xFA, 1); // ACK + controller_enQ(BX_KEY_THIS s.mouse.get_status_byte(), 1); // status + controller_enQ(BX_KEY_THIS s.mouse.get_resolution_byte(), 1); // resolution + controller_enQ(BX_KEY_THIS s.mouse.sample_rate, 1); // sample rate + BX_DEBUG(("[mouse] Get mouse information")); + break; + + case 0xeb: // Read Data (send a packet when in Remote Mode) + controller_enQ(0xFA, 1); // ACK + // perhaps we should be adding some movement here. + mouse_enQ_packet( ((BX_KEY_THIS s.mouse.button_status & 0x0f) | 0x08), + 0x00, 0x00 ); // bit3 of first byte always set + //assumed we really aren't in polling mode, a rather odd assumption. + BX_ERROR(("[mouse] Warning: Read Data command partially supported.")); + break; + + default: + //FEh Resend + BX_PANIC(("MOUSE: kbd_ctrl_to_mouse(%02xh)", (unsigned) value)); + } + } +} + +void +bx_keyb_c::create_mouse_packet(bool force_enq) { + Bit8u b1, b2, b3; + + // BX_DEBUG("Calling create_mouse_packet: force_enq=%d\n",force_enq); + + if(BX_KEY_THIS s.mouse_internal_buffer.num_elements && !force_enq) + return; + + // BX_DEBUG("Got to first milestone: force_enq=%d\n",force_enq); + + Bit16s delta_x = BX_KEY_THIS s.mouse.delayed_dx; + Bit16s delta_y = BX_KEY_THIS s.mouse.delayed_dy; + Bit8u button_state=BX_KEY_THIS s.mouse.button_status | 0x08; + + if(!force_enq && !delta_x && !delta_y) { + return; + } + + // BX_DEBUG("Got to second milestone: delta_x=%d, delta_y=%d\n",delta_x,delta_y); + + if(delta_x>254) delta_x=254; + if(delta_x<-254) delta_x=-254; + if(delta_y>254) delta_y=254; + if(delta_y<-254) delta_y=-254; + + b1 = (button_state & 0x0f) | 0x08; // bit3 always set + + if ( (delta_x>=0) && (delta_x<=255) ) { + b2 = (Bit8u) delta_x; + BX_KEY_THIS s.mouse.delayed_dx-=delta_x; + } + else if ( delta_x > 255 ) { + b2 = (Bit8u) 0xff; + BX_KEY_THIS s.mouse.delayed_dx-=255; + } + else if ( delta_x >= -256 ) { + b2 = (Bit8u) delta_x; + b1 |= 0x10; + BX_KEY_THIS s.mouse.delayed_dx-=delta_x; + } + else { + b2 = (Bit8u) 0x00; + b1 |= 0x10; + BX_KEY_THIS s.mouse.delayed_dx+=256; + } + + if ( (delta_y>=0) && (delta_y<=255) ) { + b3 = (Bit8u) delta_y; + BX_KEY_THIS s.mouse.delayed_dy-=delta_y; + } + else if ( delta_y > 255 ) { + b3 = (Bit8u) 0xff; + BX_KEY_THIS s.mouse.delayed_dy-=255; + } + else if ( delta_y >= -256 ) { + b3 = (Bit8u) delta_y; + b1 |= 0x20; + BX_KEY_THIS s.mouse.delayed_dy-=delta_y; + } + else { + b3 = (Bit8u) 0x00; + b1 |= 0x20; + BX_KEY_THIS s.mouse.delayed_dy+=256; + } + mouse_enQ_packet(b1, b2, b3); +} + + +void +bx_keyb_c::mouse_enabled_changed(bool enabled) { + if(s.mouse.delayed_dx || BX_KEY_THIS s.mouse.delayed_dy) { + create_mouse_packet(1); + } + s.mouse.delayed_dx=0; + s.mouse.delayed_dy=0; + BX_DEBUG(("Keyboard mouse disable called.")); +} + + void +bx_keyb_c::mouse_motion(int delta_x, int delta_y, unsigned button_state) +{ + bool force_enq=0; + + // If mouse events are disabled on the GUI headerbar, don't + // generate any mouse data + if (bx_options.Omouse_enabled->get () == 0) + return; + + + // don't generate interrupts if we are in remote mode. + if ( BX_KEY_THIS s.mouse.mode == MOUSE_MODE_REMOTE) + // is there any point in doing any work if we don't act on the result + // so go home. + return; + + + // Note: enable only applies in STREAM MODE. + if ( BX_KEY_THIS s.mouse.enable==0 ) + return; + + // scale down the motion + if ( (delta_x < -1) || (delta_x > 1) ) + delta_x /= 2; + if ( (delta_y < -1) || (delta_y > 1) ) + delta_y /= 2; + +#ifdef VERBOSE_KBD_DEBUG + if (delta_x != 0 || delta_y != 0) + BX_DEBUG(("[mouse] Dx=%d Dy=%d", delta_x, delta_y)); +#endif /* ifdef VERBOSE_KBD_DEBUG */ + + if( (delta_x==0) && (delta_y==0) && (BX_KEY_THIS s.mouse.button_status == (button_state & 0x3) ) ) { + BX_DEBUG(("Ignoring useless mouse_motion call:\n")); + BX_DEBUG(("This should be fixed in the gui code.\n")); + return; + } + + if(BX_KEY_THIS s.mouse.button_status != (button_state & 0x3)) { + force_enq=1; + } + + BX_KEY_THIS s.mouse.button_status = button_state & 0x3; + + if(delta_x>255) delta_x=255; + if(delta_y>255) delta_y=255; + if(delta_x<-256) delta_x=-256; + if(delta_y<-256) delta_y=-256; + + BX_KEY_THIS s.mouse.delayed_dx+=delta_x; + BX_KEY_THIS s.mouse.delayed_dy+=delta_y; + + if((BX_KEY_THIS s.mouse.delayed_dx>255)|| + (BX_KEY_THIS s.mouse.delayed_dx<-256)|| + (BX_KEY_THIS s.mouse.delayed_dy>255)|| + (BX_KEY_THIS s.mouse.delayed_dy<-256)) { + force_enq=1; + } + + create_mouse_packet(force_enq); +} + + + int +bx_keyb_c::SaveState( class state_file *fd ) +{ + fd->write_check ("keyboard start"); + fd->write (&BX_KEY_THIS s, sizeof (BX_KEY_THIS s)); + fd->write_check ("keyboard end"); + return(0); +} + + + int +bx_keyb_c::LoadState( class state_file *fd ) +{ + fd->read_check ("keyboard start"); + fd->read (&BX_KEY_THIS s, sizeof (BX_KEY_THIS s)); + fd->read_check ("keyboard end"); + return(0); +} + |