// Copyright (C) 2001 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 // 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) #include "bochs.h" #define LOG_THIS bx_keyboard. #define VERBOSE_KBD_DEBUG 0 bx_keyb_c bx_keyboard; #if BX_USE_KEY_SMF #define this (&bx_keyboard) #endif bx_keyb_c::bx_keyb_c(void) { // constructor // should zero out state info here??? memset( &s, 0, sizeof(s) ); BX_KEY_THIS setprefix("KBD"); BX_KEY_THIS 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(Boolean powerup) { Bit32u i; BX_KEY_THIS s.kbd_internal_buffer.num_elements = 0; for (i=0; iregister_irq(1, "8042 Keyboard controller"); BX_KEY_THIS devices->register_io_read_handler(this, read_handler, 0x0060, "8042 Keyboard controller"); BX_KEY_THIS devices->register_io_read_handler(this, read_handler, 0x0064, "8042 Keyboard controller"); BX_KEY_THIS devices->register_io_write_handler(this, write_handler, 0x0060, "8042 Keyboard controller"); BX_KEY_THIS devices->register_io_write_handler(this, write_handler, 0x0064, "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 ())); 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; for (i=0; is.reg[0x14] |= 0x04; BX_DEBUG(("Init.")); } #define RETURN(x) do { ret = (x); goto read_return; } while (0) // 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 Bit32u ret = 0; if (io_len > 1) BX_PANIC(("kbd: io read to address %08x, len=%u", (unsigned) address, (unsigned) io_len)); //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; 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= 2 else if (address == 0x64) { /* status register */ Bit8u val; 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; 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 */ else { BX_PANIC(("unknown address in io read to keyboard port %x", (unsigned) address)); RETURN(0); /* keep compiler happy */ } read_return: BX_DEBUG(("keyboard: 8-bit read from %04x = %02x", (unsigned)address, ret)); return ret; } // 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; if (io_len > 1) BX_PANIC(("kbd: io write to address %08x, len=%u", (unsigned) address, (unsigned) io_len)); BX_DEBUG(("keyboard: 8-bit write to %04x = %02x", (unsigned)address, (unsigned)value)); //BX_DEBUG(("WRITE(%02x) = %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 { Boolean 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.scan_convert = 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; 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.scan_convert << 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 0xa1: BX_ERROR(("Dummy out Green PC for now : 0xa1")); 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")); // 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; } // (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_ERROR(("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 case 0xdd: // Disable A20 Address Line case 0xdf: // Enable A20 Address Line 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; ireset(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()")); } } void bx_keyb_c::gen_scancode(Bit32u key) { Bit8u scancode; int extended; BX_DEBUG(( "gen_scancode %lld %x", bx_pc_system.time_ticks(), key)); if (!BX_KEY_THIS s.kbd_controller.scan_convert) BX_PANIC(("keyboard: gen_scancode with scan_convert cleared")); BX_DEBUG(("gen_scancode(): scancode: %08x", (unsigned) key)); // 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; // should deal with conversions from KSCAN to system scan codes here extended = 0; switch (key & 0xff) { case BX_KEY_CTRL_L: scancode = 0x1d; break; case BX_KEY_CTRL_R: extended = 1; scancode = 0x1d; break; case BX_KEY_SHIFT_L: scancode = 0x2a; break; case BX_KEY_SHIFT_R: scancode = 0x36; break; case BX_KEY_ESC: scancode = 0x01; break; case BX_KEY_ALT_L: scancode = 0x38; break; case BX_KEY_ALT_R: extended = 1; scancode = 0x38; break; case BX_KEY_A: scancode = 0x1e; break; case BX_KEY_B: scancode = 0x30; break; case BX_KEY_C: scancode = 0x2e; break; case BX_KEY_D: scancode = 0x20; break; case BX_KEY_E: scancode = 0x12; break; case BX_KEY_F: scancode = 0x21; break; case BX_KEY_G: scancode = 0x22; break; case BX_KEY_H: scancode = 0x23; break; case BX_KEY_I: scancode = 0x17; break; case BX_KEY_J: scancode = 0x24; break; case BX_KEY_K: scancode = 0x25; break; case BX_KEY_L: scancode = 0x26; break; case BX_KEY_M: scancode = 0x32; break; case BX_KEY_N: scancode = 0x31; break; case BX_KEY_O: scancode = 0x18; break; case BX_KEY_P: scancode = 0x19; break; case BX_KEY_Q: scancode = 0x10; break; case BX_KEY_R: scancode = 0x13; break; case BX_KEY_S: scancode = 0x1f; break; case BX_KEY_T: scancode = 0x14; break; case BX_KEY_U: scancode = 0x16; break; case BX_KEY_V: scancode = 0x2f; break; case BX_KEY_W: scancode = 0x11; break; case BX_KEY_X: scancode = 0x2d; break; case BX_KEY_Y: scancode = 0x15; break; case BX_KEY_Z: scancode = 0x2c; break; case BX_KEY_0: scancode = 0x0b; break; case BX_KEY_1: scancode = 0x02; break; case BX_KEY_2: scancode = 0x03; break; case BX_KEY_3: scancode = 0x04; break; case BX_KEY_4: scancode = 0x05; break; case BX_KEY_5: scancode = 0x06; break; case BX_KEY_6: scancode = 0x07; break; case BX_KEY_7: scancode = 0x08; break; case BX_KEY_8: scancode = 0x09; break; case BX_KEY_9: scancode = 0x0a; break; case BX_KEY_SPACE: scancode = 0x39; break; case BX_KEY_SINGLE_QUOTE: scancode = 0x28; break; case BX_KEY_COMMA: scancode = 0x33; break; case BX_KEY_PERIOD: scancode = 0x34; break; case BX_KEY_SLASH: scancode = 0x35; break; case BX_KEY_SEMICOLON: scancode = 0x27; break; case BX_KEY_EQUALS: scancode = 0x0d; break; case BX_KEY_LEFT_BRACKET: scancode = 0x1a; break; case BX_KEY_BACKSLASH: scancode = 0x2b; break; case BX_KEY_RIGHT_BRACKET: scancode = 0x1b; break; case BX_KEY_MINUS: scancode = 0x0c; break; case BX_KEY_GRAVE: scancode = 0x29; break; case BX_KEY_BACKSPACE: scancode = 0x0e; break; case BX_KEY_KP_ENTER: case BX_KEY_ENTER: scancode = 0x1c; break; case BX_KEY_TAB: scancode = 0x0f; break; case BX_KEY_LEFT: extended = 1; case BX_KEY_KP_LEFT: scancode = 0x4b; break; case BX_KEY_RIGHT: extended = 1; case BX_KEY_KP_RIGHT: scancode = 0x4d; break; case BX_KEY_UP: extended = 1; case BX_KEY_KP_UP: scancode = 0x48; break; case BX_KEY_DOWN: extended = 1; case BX_KEY_KP_DOWN: scancode = 0x50; break; case BX_KEY_INSERT: extended = 1; case BX_KEY_KP_INSERT: scancode = 0x52; break; case BX_KEY_DELETE: extended = 1; case BX_KEY_KP_DELETE: scancode = 0x53; break; case BX_KEY_HOME: extended = 1; case BX_KEY_KP_HOME: scancode = 0x47; break; case BX_KEY_END: extended = 1; case BX_KEY_KP_END: scancode = 0x4f; break; case BX_KEY_PAGE_UP: extended = 1; case BX_KEY_KP_PAGE_UP: scancode = 0x49; break; case BX_KEY_PAGE_DOWN: extended = 1; case BX_KEY_KP_PAGE_DOWN: scancode = 0x51; break; case BX_KEY_KP_ADD: scancode = 0x4e; break; case BX_KEY_KP_SUBTRACT: scancode = 0x4a; break; case BX_KEY_KP_5: scancode = 0x4c; break; case BX_KEY_KP_MULTIPLY: BX_DEBUG(( "Grey Multiply key not on 83-key keyboard" )); return; case BX_KEY_KP_DIVIDE: BX_DEBUG(( "Grey Divide key not on 83-key keyboard" )); return; case BX_KEY_NUM_LOCK: scancode = 0x45; break; case BX_KEY_F1: scancode = 0x3b; break; case BX_KEY_F2: scancode = 0x3c; break; case BX_KEY_F3: scancode = 0x3d; break; case BX_KEY_F4: scancode = 0x3e; break; case BX_KEY_F5: scancode = 0x3f; break; case BX_KEY_F6: scancode = 0x40; break; case BX_KEY_F7: scancode = 0x41; break; case BX_KEY_F8: scancode = 0x42; break; case BX_KEY_F9: scancode = 0x43; break; case BX_KEY_F10: scancode = 0x44; break; case BX_KEY_F11: scancode = 0x57; break; case BX_KEY_F12: scancode = 0x58; break; default: BX_DEBUG(( "bx_keyb_c::gen_scancode : Unhandled %u", (unsigned) key)); return; } if (extended) kbd_enQ(0xE0); if (key & BX_KEY_RELEASED) scancode |= 0x80; kbd_enQ(scancode); } void bx_keyb_c::set_kbd_clock_enable(Bit8u value) { Boolean 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) { Boolean 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(("enQ(%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(("enQ: putting scancode %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); } Boolean 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")); 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")); break; case 1: BX_INFO(("setting delay to 250 mS")); break; case 2: BX_INFO(("setting delay to 250 mS")); break; case 3: BX_INFO(("setting delay to 250 mS")); break; } BX_KEY_THIS s.kbd_internal_buffer.repeat_rate = value & 0x1f; BX_INFO(("setting repeat rate to %u", (unsigned) value)); 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_INFO(("LED status set to %02x", (unsigned) BX_KEY_THIS s.kbd_internal_buffer.led_status)); kbd_enQ(0xFA); // send ACK %%% 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(0xFA); // send ACK %%% return; break; case 0xee: // echo kbd_enQ(0xEE); // return same byte (EEh) as echo diagnostic return; break; case 0xf2: // identify keyboard BX_INFO(("identify keyboard command received")); kbd_enQ(0xFA); // AT sends ACK, MFII sends ACK+ABh+41h 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: // flush scancodes buffer and modes, then enable keyboard resetinternals(0); kbd_enQ(0xFA); // send ACK BX_KEY_THIS s.kbd_internal_buffer.scanning_enabled = 1; 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_INFO(("rest command received")); 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_INFO(("kbd_ctrl_to_kbd(): got value of %02x", (unsigned) value)); kbd_enQ(0xFA); /* send ACK ??? */ return; break; } } unsigned bx_keyb_c::periodic( Bit32u usec_delta ) { static int multiple=0; Bit8u retval; UNUSED( usec_delta ); if ( ++multiple==10) { multiple=0; bx_gui.handle_events(); } 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 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 (); } } 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::mouse_motion(int delta_x, int delta_y, unsigned button_state) { Bit8u b1, b2, b3; // 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 */ b1 = (button_state & 0x0f) | 0x08; // bit3 always set BX_KEY_THIS s.mouse.button_status = button_state & 0x3; if ( (delta_x>=0) && (delta_x<=255) ) { b2 = delta_x; } else if ( delta_x > 255 ) { b2 = 0xff; } else if ( delta_x >= -256 ) { b2 = delta_x; b1 |= 0x10; } else { b2 = 0x00; b1 |= 0x10; } if ( (delta_y>=0) && (delta_y<=255) ) { b3 = delta_y; } else if ( delta_y > 255 ) { b3 = 0xff; } else if ( delta_y >= -256 ) { b3 = delta_y; b1 |= 0x20; } else { b3 = 0x00; b1 |= 0x20; } mouse_enQ_packet(b1, b2, b3); } void bx_keyb_c::put_scancode( unsigned char *code, int count ) { for ( int i = 0 ; i < count ; i++ ) { kbd_enQ( code[i] ); } return; } 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); }