///////////////////////////////////////////////////////////////////////// // $Id$ ///////////////////////////////////////////////////////////////////////// // // Copyright (C) 2002-2009 The Bochs Project // // 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 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 // 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 // // 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 "iodev.h" #include "gui/keymap.h" #include #include "keyboard.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) { delete theKeyboard; } bx_keyb_c::bx_keyb_c() { put("KBD"); pastebuf = NULL; } bx_keyb_c::~bx_keyb_c() { // remove runtime parameter handler SIM->get_param_num(BXPN_KBD_PASTE_DELAY)->set_handler(NULL); if (pastebuf != NULL) { delete [] pastebuf; } #if BX_WITH_WX bx_list_c *list = (bx_list_c*)SIM->get_param(BXPN_WX_KBD_STATE); if (list != NULL) { list->clear(); } #endif BX_DEBUG(("Exit")); } // flush internal buffer and reset keyboard settings to power-up condition void bx_keyb_c::resetinternals(bx_bool powerup) { BX_KEY_THIS s.kbd_internal_buffer.num_elements = 0; for (int i=0; iget_param_num(BXPN_KBD_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; iget_param_enum(BXPN_MOUSE_TYPE)->get(); 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; BX_KEY_THIS s.mouse.delayed_dz = 0; BX_KEY_THIS s.mouse.im_request = 0; // wheel mouse mode request BX_KEY_THIS s.mouse.im_mode = 0; // wheel mouse mode for (i=0; iget_param_num(BXPN_KBD_PASTE_DELAY)->get()); BX_KEY_THIS paste_service = 0; BX_KEY_THIS stop_paste = 0; // mouse port installed on system board DEV_cmos_set_reg(0x14, DEV_cmos_get_reg(0x14) | 0x04); // add keyboard LEDs to the statusbar BX_KEY_THIS statusbar_id[0] = bx_gui->register_statusitem("NUM"); BX_KEY_THIS statusbar_id[1] = bx_gui->register_statusitem("CAPS"); BX_KEY_THIS statusbar_id[2] = bx_gui->register_statusitem("SCRL"); #if BX_WITH_WX bx_param_num_c *param; bx_list_c *list; if (SIM->get_param("wxdebug") != NULL) { // register shadow params (Experimental, not a complete list by far) list = (bx_list_c*)SIM->get_param(BXPN_WX_KBD_STATE); if (list == NULL) { list = new bx_list_c(SIM->get_param("wxdebug"), "keyboard", "Keyboard State", 20); } new bx_shadow_bool_c(list, "irq1_req", "Keyboard IRQ1 requested", &BX_KEY_THIS s.kbd_controller.irq1_requested); new bx_shadow_bool_c(list, "irq12_req", "Keyboard IRQ12 requested", &BX_KEY_THIS s.kbd_controller.irq12_requested); param = new bx_shadow_num_c(list, "timer_pending", &BX_KEY_THIS s.kbd_controller.timer_pending); param->set_label("Keyboard timer pending"); new bx_shadow_bool_c(list, "pare", "Keyboard PARE", &BX_KEY_THIS s.kbd_controller.pare); new bx_shadow_bool_c(list, "tim", "Keyboard TIM", &BX_KEY_THIS s.kbd_controller.tim); new bx_shadow_bool_c(list, "auxb", "Keyboard AUXB", &BX_KEY_THIS s.kbd_controller.auxb); new bx_shadow_bool_c(list, "keyl", "Keyboard KEYL", &BX_KEY_THIS s.kbd_controller.keyl); new bx_shadow_bool_c(list, "c_d", "Keyboard C_D", &BX_KEY_THIS s.kbd_controller.c_d); new bx_shadow_bool_c(list, "sysf", "Keyboard SYSF", &BX_KEY_THIS s.kbd_controller.sysf); new bx_shadow_bool_c(list, "inpb", "Keyboard INPB", &BX_KEY_THIS s.kbd_controller.inpb); new bx_shadow_bool_c(list, "outb", "Keyboard OUTB", &BX_KEY_THIS s.kbd_controller.outb); } #endif if ((BX_KEY_THIS s.mouse.type == BX_MOUSE_TYPE_PS2) || (BX_KEY_THIS s.mouse.type == BX_MOUSE_TYPE_IMPS2)) { DEV_register_default_mouse(this, mouse_enq_static, mouse_enabled_changed_static); } // init runtime parameter SIM->get_param_num(BXPN_KBD_PASTE_DELAY)->set_handler(kbd_param_handler); SIM->get_param_num(BXPN_KBD_PASTE_DELAY)->set_runtime_param(1); SIM->get_param_num(BXPN_MOUSE_ENABLED)->set_handler(kbd_param_handler); SIM->get_param_num(BXPN_MOUSE_ENABLED)->set_runtime_param(1); } void bx_keyb_c::reset(unsigned type) { if (BX_KEY_THIS pastebuf != NULL) { BX_KEY_THIS stop_paste = 1; } } void bx_keyb_c::register_state(void) { int i; char name[4]; bx_list_c *buffer; bx_list_c *list = new bx_list_c(SIM->get_bochs_root(), "keyboard", "Keyboard State", 7); bx_list_c *ctrl = new bx_list_c(list, "controller", 23); BXRS_PARAM_BOOL(ctrl, tim, BX_KEY_THIS s.kbd_controller.tim); BXRS_PARAM_BOOL(ctrl, auxb, BX_KEY_THIS s.kbd_controller.auxb); BXRS_PARAM_BOOL(ctrl, c_d, BX_KEY_THIS s.kbd_controller.c_d); BXRS_PARAM_BOOL(ctrl, sysf, BX_KEY_THIS s.kbd_controller.sysf); BXRS_PARAM_BOOL(ctrl, inpb, BX_KEY_THIS s.kbd_controller.inpb); BXRS_PARAM_BOOL(ctrl, outb, BX_KEY_THIS s.kbd_controller.outb); BXRS_PARAM_BOOL(ctrl, kbd_clock_enabled, BX_KEY_THIS s.kbd_controller.kbd_clock_enabled); BXRS_PARAM_BOOL(ctrl, aux_clock_enabled, BX_KEY_THIS s.kbd_controller.aux_clock_enabled); BXRS_PARAM_BOOL(ctrl, allow_irq1, BX_KEY_THIS s.kbd_controller.allow_irq1); BXRS_PARAM_BOOL(ctrl, allow_irq12, BX_KEY_THIS s.kbd_controller.allow_irq12); BXRS_HEX_PARAM_FIELD(ctrl, kbd_output_buffer, BX_KEY_THIS s.kbd_controller.kbd_output_buffer); BXRS_HEX_PARAM_FIELD(ctrl, aux_output_buffer, BX_KEY_THIS s.kbd_controller.aux_output_buffer); BXRS_HEX_PARAM_FIELD(ctrl, last_comm, BX_KEY_THIS s.kbd_controller.last_comm); BXRS_DEC_PARAM_FIELD(ctrl, expecting_port60h, BX_KEY_THIS s.kbd_controller.expecting_port60h); BXRS_DEC_PARAM_FIELD(ctrl, expecting_mouse_parameter, BX_KEY_THIS s.kbd_controller.expecting_mouse_parameter); BXRS_HEX_PARAM_FIELD(ctrl, last_mouse_command, BX_KEY_THIS s.kbd_controller.last_mouse_command); BXRS_DEC_PARAM_FIELD(ctrl, timer_pending, BX_KEY_THIS s.kbd_controller.timer_pending); BXRS_PARAM_BOOL(ctrl, irq1_requested, BX_KEY_THIS s.kbd_controller.irq1_requested); BXRS_PARAM_BOOL(ctrl, irq12_requested, BX_KEY_THIS s.kbd_controller.irq12_requested); BXRS_PARAM_BOOL(ctrl, scancodes_translate, BX_KEY_THIS s.kbd_controller.scancodes_translate); BXRS_PARAM_BOOL(ctrl, expecting_scancodes_set, BX_KEY_THIS s.kbd_controller.expecting_scancodes_set); BXRS_DEC_PARAM_FIELD(ctrl, current_scancodes_set, BX_KEY_THIS s.kbd_controller.current_scancodes_set); BXRS_PARAM_BOOL(ctrl, bat_in_progress, BX_KEY_THIS s.kbd_controller.bat_in_progress); bx_list_c *mouse = new bx_list_c(list, "mouse", 12); BXRS_DEC_PARAM_FIELD(mouse, sample_rate, BX_KEY_THIS s.mouse.sample_rate); BXRS_DEC_PARAM_FIELD(mouse, resolution_cpmm, BX_KEY_THIS s.mouse.resolution_cpmm); BXRS_DEC_PARAM_FIELD(mouse, scaling, BX_KEY_THIS s.mouse.scaling); BXRS_DEC_PARAM_FIELD(mouse, mode, BX_KEY_THIS s.mouse.mode); BXRS_DEC_PARAM_FIELD(mouse, saved_mode, BX_KEY_THIS s.mouse.saved_mode); BXRS_PARAM_BOOL(mouse, enable, BX_KEY_THIS s.mouse.enable); BXRS_DEC_PARAM_FIELD(mouse, button_status, BX_KEY_THIS s.mouse.button_status); BXRS_DEC_PARAM_FIELD(mouse, delayed_dx, BX_KEY_THIS s.mouse.delayed_dx); BXRS_DEC_PARAM_FIELD(mouse, delayed_dy, BX_KEY_THIS s.mouse.delayed_dy); BXRS_DEC_PARAM_FIELD(mouse, delayed_dz, BX_KEY_THIS s.mouse.delayed_dz); BXRS_DEC_PARAM_FIELD(mouse, im_request, BX_KEY_THIS s.mouse.im_request); BXRS_PARAM_BOOL(mouse, im_mode, BX_KEY_THIS s.mouse.im_mode); bx_list_c *kbdbuf = new bx_list_c(list, "kbd_internal_buffer", 9); BXRS_DEC_PARAM_FIELD(kbdbuf, num_elements, BX_KEY_THIS s.kbd_internal_buffer.num_elements); buffer = new bx_list_c(kbdbuf, "buffer", BX_KBD_ELEMENTS); for (i=0; istatusbar_setitem(BX_KEY_THIS statusbar_id[0], value & 0x02); bx_gui->statusbar_setitem(BX_KEY_THIS statusbar_id[1], value & 0x04); bx_gui->statusbar_setitem(BX_KEY_THIS statusbar_id[2], value & 0x01); } } Bit64s bx_keyb_c::kbd_param_handler(bx_param_c *param, int set, Bit64s val) { if (set) { char pname[BX_PATHNAME_LEN]; param->get_param_path(pname, BX_PATHNAME_LEN); if (!strcmp(pname, BXPN_KBD_PASTE_DELAY)) { BX_KEY_THIS paste_delay_changed((Bit32u)val); } else if (!strcmp(pname, BXPN_MOUSE_ENABLED)) { if (set) { bx_gui->mouse_enabled_changed(val!=0); DEV_mouse_enabled_changed(val!=0); } } else { BX_PANIC(("kbd_param_handler called with unexpected parameter '%s'", pname)); } } return val; } void bx_keyb_c::paste_delay_changed(Bit32u value) { BX_KEY_THIS pastedelay = value / 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 Bit8u val; if (address == 0x60) { /* output buffer */ 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_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_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= 2 else if (address == 0x64) { /* status register */ val = (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; BX_KEY_THIS s.kbd_controller.tim = 0; return val; } #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_INFO(("keyboard: scan convert turned off")); // (mch) NT needs this BX_KEY_THIS s.kbd_controller.scancodes_translate = scan_convert; } break; case 0xcb: // write keyboard controller mode BX_DEBUG(("write keyboard controller mode with value %02xh", (unsigned) value)); break; case 0xd1: // write output port BX_DEBUG(("write output port with value %02xh", (unsigned) value)); BX_DEBUG(("write output port : %sable A20",(value & 0x02)?"en":"dis")); BX_SET_ENABLE_A20((value & 0x02) != 0); if (!(value & 0x01)) { BX_INFO(("write output port : processor reset requested!")); bx_pc_system.Reset(BX_RESET_SOFTWARE); } 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) { set_kbd_clock_enable(1); } 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 0xaf: // get controller version BX_INFO(("'get controller version' not supported yet")); 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 not inhibited controller_enQ(0x80, 0); break; case 0xca: // read keyboard controller mode controller_enQ(0x01, 0); // PS/2 (MCA)interface break; case 0xcb: // write keyboard controller mode BX_DEBUG(("write keyboard controller mode")); // write keyboard controller mode to bit 0 of port 0x60 BX_KEY_THIS s.kbd_controller.expecting_port60h = 1; 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.irq12_requested << 5) | (BX_KEY_THIS s.kbd_controller.irq1_requested << 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 (cpu?) Reset, transition to real mode BX_INFO(("io write 0x64: command 0xfe: reset cpu")); bx_pc_system.Reset(BX_RESET_SOFTWARE); 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_ERROR(("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; BX_KEY_THIS paste_service = 1; 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) { BX_KEY_THIS paste_service = 0; 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; BX_KEY_THIS paste_service = 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; if ((BX_KEY_THIS pastebuf != NULL) && (!BX_KEY_THIS paste_service)) { BX_KEY_THIS stop_paste = 1; return; } 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 we have a removable keyboard installed, we need to call its handler first if (DEV_optional_key_enq(scancode)) { return; } if (BX_KEY_THIS s.kbd_controller.scancodes_translate) { // Translate before send Bit8u escaped=0x00; for (i=0; i= 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_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_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) { 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 */ /* int 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_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_bool bx_keyb_c::mouse_enQ_packet(Bit8u b1, Bit8u b2, Bit8u b3, Bit8u b4) { int bytes = 3; if (BX_KEY_THIS s.mouse.im_mode) bytes = 4; if ((BX_KEY_THIS s.mouse_internal_buffer.num_elements + bytes) >= BX_MOUSE_BUFF_SIZE) { return(0); /* buffer doesn't have the space */ } mouse_enQ(b1); mouse_enQ(b2); mouse_enQ(b3); if (BX_KEY_THIS s.mouse.im_mode) mouse_enQ(b4); 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; } /* 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(); return; } } 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)); bx_gui->statusbar_setitem(BX_KEY_THIS statusbar_id[0], value & 0x02); bx_gui->statusbar_setitem(BX_KEY_THIS statusbar_id[1], value & 0x04); bx_gui->statusbar_setitem(BX_KEY_THIS statusbar_id[2], value & 0x01); 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", (unsigned) BX_KEY_THIS s.kbd_controller.current_scancodes_set + 1)); kbd_enQ(0xFA); } else { BX_ERROR(("Received scancodes set out of range: %d", value)); kbd_enQ(0xFF); // send ERROR } } else { // Send ACK (SF patch #1159626) kbd_enQ(0xFA); // 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 %%% break; case 0x05: // ??? // (mch) trying to get this to work... BX_KEY_THIS s.kbd_controller.sysf = 1; kbd_enQ_imm(0xfe); break; case 0xed: // LED Write BX_KEY_THIS s.kbd_internal_buffer.expecting_led_write = 1; kbd_enQ_imm(0xFA); // send ACK %%% break; case 0xee: // echo kbd_enQ(0xEE); // return same byte (EEh) as echo diagnostic break; case 0xf0: // Select alternate scan code set BX_KEY_THIS s.kbd_controller.expecting_scancodes_set = 1; BX_DEBUG(("Expecting scancode set info...")); kbd_enQ(0xFA); // send ACK 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 (SIM->get_param_enum(BXPN_KBD_TYPE)->get() != BX_KBD_XT_TYPE) { kbd_enQ(0xFA); if (SIM->get_param_enum(BXPN_KBD_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); } } 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 break; case 0xf4: // enable keyboard BX_KEY_THIS s.kbd_internal_buffer.scanning_enabled = 1; kbd_enQ(0xFA); // send ACK 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")); 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")); break; case 0xfe: // resend. aiiee. BX_PANIC(("got 0xFE (resend)")); break; case 0xff: // reset: internal keyboard reset and afterwards the BAT BX_DEBUG(("reset command received")); resetinternals(1); kbd_enQ(0xFA); // send ACK BX_KEY_THIS s.kbd_controller.bat_in_progress = 1; kbd_enQ(0xAA); // BAT test passed break; case 0xd3: kbd_enQ(0xfa); 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 default: BX_ERROR(("kbd_ctrl_to_kbd(): got value of 0x%02x", value)); kbd_enQ(0xFE); /* send NACK */ 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(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_internal_buffer.num_elements && (BX_KEY_THIS s.kbd_controller.kbd_clock_enabled || BX_KEY_THIS s.kbd_controller.bat_in_progress)) { 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_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_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(("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_KEY_THIS s.kbd_controller.outb = 1; BX_KEY_THIS s.kbd_controller.auxb = 1; 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--; 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 = 1; } } void bx_keyb_c::kbd_ctrl_to_mouse(Bit8u value) { // if we are not using a ps2 mouse, some of the following commands need to return different values bx_bool is_ps2 = 0; if ((BX_KEY_THIS s.mouse.type == BX_MOUSE_TYPE_PS2) || (BX_KEY_THIS s.mouse.type == BX_MOUSE_TYPE_IMPS2)) is_ps2 = 1; 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)); // 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)); if ((value == 200) && (!BX_KEY_THIS s.mouse.im_request)) { BX_KEY_THIS s.mouse.im_request = 1; } else if ((value == 100) && (BX_KEY_THIS s.mouse.im_request == 1)) { BX_KEY_THIS s.mouse.im_request = 2; } else if ((value == 80) && (BX_KEY_THIS s.mouse.im_request == 2)) { if (BX_KEY_THIS s.mouse.type == BX_MOUSE_TYPE_IMPS2) { BX_INFO(("wheel mouse mode enabled")); BX_KEY_THIS s.mouse.im_mode = 1; } else { BX_INFO(("wheel mouse mode request rejected")); } BX_KEY_THIS s.mouse.im_request = 0; } else { BX_KEY_THIS s.mouse.im_request = 0; } 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)) { BX_DEBUG(("mouse: wrap mode: ignoring command 0x%02x",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 BX_DEBUG(("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) { BX_DEBUG(("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. BX_DEBUG(("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.) BX_DEBUG(("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 if (BX_KEY_THIS s.mouse.im_mode) controller_enQ(0x03, 1); // Device ID (wheel z-mouse) else controller_enQ(0x00, 1); // Device ID (standard) 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) // is a mouse present? if (is_ps2) { BX_KEY_THIS s.mouse.enable = 1; controller_enQ(0xFA, 1); // ACK BX_DEBUG(("mouse enabled (stream mode)")); } else { // a mouse isn't present. We need to return a 0xFE (resend) instead of a 0xFA (ACK) controller_enQ(0xFE, 1); // RESEND BX_KEY_THIS s.kbd_controller.tim = 1; } break; case 0xf5: // Disable (in stream mode) BX_KEY_THIS s.mouse.enable = 0; controller_enQ(0xFA, 1); // ACK BX_DEBUG(("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 // is a mouse present? if (is_ps2) { 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; if (BX_KEY_THIS s.mouse.im_mode) BX_INFO(("wheel mouse mode disabled")); BX_KEY_THIS s.mouse.im_mode = 0; /* (mch) NT expects an ack here */ controller_enQ(0xFA, 1); // ACK controller_enQ(0xAA, 1); // completion code controller_enQ(0x00, 1); // ID code (standard after reset) BX_DEBUG(("mouse reset")); } else { // a mouse isn't present. We need to return a 0xFE (resend) instead of a 0xFA (ACK) controller_enQ(0xFE, 1); // RESEND BX_KEY_THIS s.kbd_controller.tim = 1; } 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, 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; case 0xbb: // OS/2 Warp 3 uses this command BX_ERROR(("mouse: ignoring 0xbb command")); break; default: // If PS/2 mouse present, send NACK for unknown commands, otherwise ignore if (is_ps2) { BX_ERROR(("kbd_ctrl_to_mouse(): got value of 0x%02x", value)); controller_enQ(0xFE, 1); /* send NACK */ } } } } void bx_keyb_c::create_mouse_packet(bx_bool force_enq) { Bit8u b1, b2, b3, b4; if(BX_KEY_THIS s.mouse_internal_buffer.num_elements && !force_enq) return; 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; } 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; } b4 = (Bit8u) -BX_KEY_THIS s.mouse.delayed_dz; mouse_enQ_packet(b1, b2, b3, b4); } void bx_keyb_c::mouse_enabled_changed_static(void *dev, bx_bool enabled) { ((bx_keyb_c*)dev)->mouse_enabled_changed(enabled); } void bx_keyb_c::mouse_enabled_changed(bx_bool enabled) { if (BX_KEY_THIS s.mouse.delayed_dx || BX_KEY_THIS s.mouse.delayed_dy || BX_KEY_THIS s.mouse.delayed_dz) { create_mouse_packet(1); } BX_KEY_THIS s.mouse.delayed_dx=0; BX_KEY_THIS s.mouse.delayed_dy=0; BX_KEY_THIS s.mouse.delayed_dz=0; BX_DEBUG(("PS/2 mouse %s", enabled?"enabled":"disabled")); } void bx_keyb_c::mouse_enq_static(void *dev, int delta_x, int delta_y, int delta_z, unsigned button_state) { ((bx_keyb_c*)dev)->mouse_motion(delta_x, delta_y, delta_z, button_state); } void bx_keyb_c::mouse_motion(int delta_x, int delta_y, int delta_z, unsigned button_state) { bx_bool force_enq=0; // 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; if (!BX_KEY_THIS s.mouse.im_mode) delta_z = 0; #ifdef VERBOSE_KBD_DEBUG if (delta_x != 0 || delta_y != 0 || delta_z != 0) BX_DEBUG(("[mouse] Dx=%d Dy=%d Dz=%d", delta_x, delta_y, delta_z)); #endif /* ifdef VERBOSE_KBD_DEBUG */ if ((delta_x==0) && (delta_y==0) && (delta_z==0) && (BX_KEY_THIS s.mouse.button_status == (button_state & 0x7))) { BX_DEBUG(("Ignoring useless mouse_motion call:")); BX_DEBUG(("This should be fixed in the gui code.")); return; } if ((BX_KEY_THIS s.mouse.button_status != (button_state & 0x7)) || delta_z) { force_enq=1; } BX_KEY_THIS s.mouse.button_status = button_state & 0x7; 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; BX_KEY_THIS s.mouse.delayed_dz = delta_z; 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); }