1 /* 2 * COPYRIGHT: GPL - See COPYING in the top level directory 3 * PROJECT: ReactOS Virtual DOS Machine 4 * FILE: subsystems/mvdm/ntvdm/bios/bios32/kbdbios32.c 5 * PURPOSE: VDM 32-bit PS/2 Keyboard BIOS 6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org> 7 */ 8 9 /* INCLUDES *******************************************************************/ 10 11 #include "ntvdm.h" 12 13 #define NDEBUG 14 #include <debug.h> 15 16 #include "kbdbios32.h" 17 #include <bios/kbdbios.h> 18 #include "bios32p.h" 19 20 #include "int32.h" 21 #include "cpu/cpu.h" // for EMULATOR_FLAG_ZF 22 #include "io.h" 23 #include "hardware/ps2.h" 24 25 /* PRIVATE VARIABLES **********************************************************/ 26 27 static BYTE BiosKeyboardMap[256]; 28 29 /* PRIVATE FUNCTIONS **********************************************************/ 30 31 static BOOLEAN BiosKbdBufferPush(WORD Data) 32 { 33 /* Get the location of the element after the tail */ 34 WORD NextElement = Bda->KeybdBufferTail + sizeof(WORD); 35 36 /* Wrap it around if it's at or beyond the end */ 37 if (NextElement >= Bda->KeybdBufferEnd) NextElement = Bda->KeybdBufferStart; 38 39 /* If it's full, fail */ 40 if (NextElement == Bda->KeybdBufferHead) 41 { 42 DPRINT1("BIOS keyboard buffer full.\n"); 43 return FALSE; 44 } 45 46 /* Put the value in the queue */ 47 *((LPWORD)((ULONG_PTR)Bda + Bda->KeybdBufferTail)) = Data; 48 Bda->KeybdBufferTail = NextElement; 49 50 /* Return success */ 51 return TRUE; 52 } 53 54 static BOOLEAN BiosKbdBufferTop(LPWORD Data) 55 { 56 /* If it's empty, fail */ 57 if (Bda->KeybdBufferHead == Bda->KeybdBufferTail) return FALSE; 58 59 /* Otherwise, get the value and return success */ 60 *Data = *((LPWORD)((ULONG_PTR)Bda + Bda->KeybdBufferHead)); 61 62 return TRUE; 63 } 64 65 static BOOLEAN BiosKbdBufferPop(VOID) 66 { 67 /* If it's empty, fail */ 68 if (Bda->KeybdBufferHead == Bda->KeybdBufferTail) return FALSE; 69 70 /* Remove the value from the queue */ 71 Bda->KeybdBufferHead += sizeof(WORD); 72 73 /* Check if we are at, or have passed, the end of the buffer */ 74 if (Bda->KeybdBufferHead >= Bda->KeybdBufferEnd) 75 { 76 /* Return it to the beginning */ 77 Bda->KeybdBufferHead = Bda->KeybdBufferStart; 78 } 79 80 /* Return success */ 81 return TRUE; 82 } 83 84 /* static */ 85 VOID WINAPI BiosKeyboardService(LPWORD Stack) 86 { 87 switch (getAH()) 88 { 89 /* Wait for keystroke and read */ 90 case 0x00: 91 /* Wait for extended keystroke and read */ 92 case 0x10: 93 { 94 WORD Character; 95 96 /* Read the character (and wait if necessary) */ 97 if (!BiosKbdBufferTop(&Character)) 98 { 99 /* No key available. Set the handler CF to repeat the BOP */ 100 setCF(1); 101 break; 102 } 103 104 if (getAH() == 0x00 && LOBYTE(Character) == 0xE0) 105 { 106 /* Clear the extended code */ 107 Character &= 0xFF00; 108 } 109 110 BiosKbdBufferPop(); 111 setAX(Character); 112 setCF(0); 113 114 break; 115 } 116 117 /* Get keystroke status */ 118 case 0x01: 119 /* Get extended keystroke status */ 120 case 0x11: 121 { 122 WORD Character; 123 124 if (BiosKbdBufferTop(&Character)) 125 { 126 /* There is a character, clear ZF and return it */ 127 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_ZF; 128 129 if (getAH() == 0x01 && LOBYTE(Character) == 0xE0) 130 { 131 /* Clear the extended code */ 132 Character &= 0xFF00; 133 } 134 135 setAX(Character); 136 } 137 else 138 { 139 /* No character, set ZF */ 140 Stack[STACK_FLAGS] |= EMULATOR_FLAG_ZF; 141 } 142 143 break; 144 } 145 146 /* Get shift status */ 147 case 0x02: 148 { 149 /* Return the lower byte of the keyboard shift status word */ 150 setAL(LOBYTE(Bda->KeybdShiftFlags)); 151 break; 152 } 153 154 /* Reserved */ 155 case 0x04: 156 { 157 DPRINT1("BIOS Function INT 16h, AH = 0x04 is RESERVED\n"); 158 break; 159 } 160 161 /* Push keystroke */ 162 case 0x05: 163 { 164 /* Return 0 if success, 1 if failure */ 165 setAL(BiosKbdBufferPush(getCX()) == FALSE); 166 break; 167 } 168 169 /* Get extended shift status */ 170 case 0x12: 171 { 172 /* 173 * Be careful! The returned word is similar to 'Bda->KeybdShiftFlags' 174 * but the high byte is organized differently: 175 * the bits 2 and 3 of the high byte are not the same: 176 * instead they correspond to the right CTRL and ALT keys as specified 177 * in bits 2 and 3 of LOBYTE(Bda->KeybdStatusFlags). 178 */ 179 // Bda->KeybdShiftFlags & 0xF3FF; 180 WORD KeybdShiftFlags = MAKEWORD(LOBYTE(Bda->KeybdShiftFlags), 181 (HIBYTE(Bda->KeybdShiftFlags ) & 0xF3) | 182 (LOBYTE(Bda->KeybdStatusFlags) & 0x0C)); 183 184 /* Return the extended keyboard shift status word */ 185 setAX(KeybdShiftFlags); 186 break; 187 } 188 189 default: 190 { 191 DPRINT1("BIOS Function INT 16h, AH = 0x%02X NOT IMPLEMENTED\n", 192 getAH()); 193 } 194 } 195 } 196 197 // Keyboard IRQ 1 198 /* static */ 199 VOID WINAPI BiosKeyboardIrq(LPWORD Stack) 200 { 201 static BOOLEAN Extended = FALSE; 202 BOOLEAN SkipScanCode; 203 BYTE ScanCode, VirtualKey; 204 WORD Character; 205 206 /* 207 * Get the scan code from the PS/2 port, then call the 208 * INT 15h, AH=4Fh Keyboard Intercept function to try to 209 * translate the scan code. CF must be set before the call. 210 * In return, if CF is set we continue processing the scan code 211 * stored in AL, and if not, we skip it. 212 */ 213 WORD AX = getAX(); 214 WORD CX = getCX(); 215 WORD DX = getDX(); 216 WORD BX = getBX(); 217 WORD BP = getBP(); 218 WORD SI = getSI(); 219 WORD DI = getDI(); 220 WORD DS = getDS(); 221 WORD ES = getES(); 222 223 setCF(1); 224 setAL(IOReadB(PS2_DATA_PORT)); 225 setAH(0x4F); 226 Int32Call(&BiosContext, BIOS_MISC_INTERRUPT); 227 228 /* Retrieve the modified scan code in AL */ 229 SkipScanCode = (getCF() == 0); 230 ScanCode = getAL(); 231 232 setAX(AX); 233 setCX(CX); 234 setDX(DX); 235 setBX(BX); 236 setBP(BP); 237 setSI(SI); 238 setDI(DI); 239 setDS(DS); 240 setES(ES); 241 setCF(0); 242 243 if (ScanCode == 0xE0) 244 { 245 Extended = TRUE; 246 Bda->KeybdStatusFlags |= 0x02; 247 goto Quit; 248 } 249 250 // FIXME: For diagnostic purposes. We should decide what to do then!! 251 if (ScanCode == 0xE1) 252 DPRINT1("BiosKeyboardIrq, ScanCode == 0xE1\n"); 253 254 /* Check whether CF is clear. If so, skip the scan code. */ 255 if (SkipScanCode) goto Quit; 256 257 /* Get the corresponding virtual key code */ 258 VirtualKey = MapVirtualKey(ScanCode & 0x7F, MAPVK_VSC_TO_VK); 259 260 /* Check if this is a key press or release */ 261 if (!(ScanCode & (1 << 7))) 262 { 263 /* Key press, set the highest bit */ 264 BiosKeyboardMap[VirtualKey] |= (1 << 7); 265 266 switch (VirtualKey) 267 { 268 case VK_NUMLOCK: 269 case VK_CAPITAL: 270 case VK_SCROLL: 271 case VK_INSERT: 272 { 273 /* For toggle keys, toggle the lowest bit in the keyboard map */ 274 BiosKeyboardMap[VirtualKey] ^= ~(1 << 0); 275 break; 276 } 277 278 case VK_SHIFT: 279 case VK_LSHIFT: 280 case VK_RSHIFT: 281 case VK_CONTROL: 282 case VK_RCONTROL: 283 case VK_LCONTROL: 284 case VK_MENU: 285 case VK_LMENU: 286 case VK_RMENU: 287 { 288 /* Modifier keys don't go in the buffer */ 289 break; 290 } 291 292 default: 293 { 294 Character = Extended ? 0xE0 : 0x00; 295 296 /* If this is not an extended scancode, and ALT isn't held down, find out which character this is */ 297 if (!Extended && !(Bda->KeybdShiftFlags & (BDA_KBDFLAG_ALT | BDA_KBDFLAG_LALT | BDA_KBDFLAG_RALT))) 298 { 299 if (ToAscii(VirtualKey, ScanCode, BiosKeyboardMap, &Character, 0) == 0) 300 { 301 /* Not ASCII */ 302 Character = 0; 303 } 304 } 305 306 /* Push it onto the BIOS keyboard queue */ 307 BiosKbdBufferPush(MAKEWORD(Character, ScanCode)); 308 } 309 } 310 } 311 else 312 { 313 /* Key release, unset the highest bit */ 314 BiosKeyboardMap[VirtualKey] &= ~(1 << 7); 315 } 316 317 /* Clear the keyboard flags */ 318 Bda->KeybdShiftFlags = 0; 319 // Release right CTRL and ALT keys 320 Bda->KeybdStatusFlags &= ~(BDA_KBDFLAG_RCTRL | BDA_KBDFLAG_RALT); 321 322 /* Set the appropriate flags based on the state */ 323 // SHIFT 324 if (BiosKeyboardMap[VK_RSHIFT] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_RSHIFT; 325 if (BiosKeyboardMap[VK_LSHIFT] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_LSHIFT; 326 if (BiosKeyboardMap[VK_SHIFT] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_LSHIFT; 327 // CTRL 328 if (BiosKeyboardMap[VK_RCONTROL] & (1 << 7)) Bda->KeybdStatusFlags |= BDA_KBDFLAG_RCTRL; 329 if (BiosKeyboardMap[VK_LCONTROL] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_LCTRL; 330 if (BiosKeyboardMap[VK_CONTROL] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_CTRL; 331 // ALT 332 if (BiosKeyboardMap[VK_RMENU] & (1 << 7)) Bda->KeybdStatusFlags |= BDA_KBDFLAG_RALT; 333 if (BiosKeyboardMap[VK_LMENU] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_LALT; 334 if (BiosKeyboardMap[VK_MENU] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_ALT; 335 // Others 336 if (BiosKeyboardMap[VK_SCROLL] & (1 << 0)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_SCROLL_ON; 337 if (BiosKeyboardMap[VK_NUMLOCK] & (1 << 0)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_NUMLOCK_ON; 338 if (BiosKeyboardMap[VK_CAPITAL] & (1 << 0)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_CAPSLOCK_ON; 339 if (BiosKeyboardMap[VK_INSERT] & (1 << 0)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_INSERT_ON; 340 if (BiosKeyboardMap[VK_SNAPSHOT] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_SYSRQ; 341 if (BiosKeyboardMap[VK_PAUSE] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_PAUSE; 342 if (BiosKeyboardMap[VK_SCROLL] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_SCROLL; 343 if (BiosKeyboardMap[VK_NUMLOCK] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_NUMLOCK; 344 if (BiosKeyboardMap[VK_CAPITAL] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_CAPSLOCK; 345 if (BiosKeyboardMap[VK_INSERT] & (1 << 7)) Bda->KeybdShiftFlags |= BDA_KBDFLAG_INSERT; 346 347 /* Clear the extended key flag */ 348 Extended = FALSE; 349 Bda->KeybdStatusFlags &= ~0x02; // Remove the 0xE0 code flag 350 // Bda->KeybdStatusFlags &= ~0x01; // Remove the 0xE1 code flag 351 352 DPRINT("BiosKeyboardIrq - Character = 0x%X, ScanCode = 0x%X, KeybdShiftFlags = 0x%X\n", 353 Character, ScanCode, Bda->KeybdShiftFlags); 354 355 Quit: 356 PicIRQComplete(LOBYTE(Stack[STACK_INT_NUM])); 357 } 358 359 /* PUBLIC FUNCTIONS ***********************************************************/ 360 361 VOID KbdBios32Post(VOID) 362 { 363 /* Initialize the BDA */ 364 Bda->KeybdBufferStart = FIELD_OFFSET(BIOS_DATA_AREA, KeybdBuffer); 365 Bda->KeybdBufferEnd = Bda->KeybdBufferStart + BIOS_KBD_BUFFER_SIZE * sizeof(WORD); 366 Bda->KeybdBufferHead = Bda->KeybdBufferTail = Bda->KeybdBufferStart; 367 368 // FIXME: Fill the keyboard buffer with invalid values for diagnostic purposes... 369 RtlFillMemory(((LPVOID)((ULONG_PTR)Bda + Bda->KeybdBufferStart)), 370 BIOS_KBD_BUFFER_SIZE * sizeof(WORD), 'A'); 371 372 Bda->KeybdShiftFlags = 0; 373 Bda->KeybdStatusFlags = (1 << 4); // 101/102 enhanced keyboard installed 374 Bda->KeybdLedFlags = 0; 375 376 /* 377 * Register the BIOS 32-bit Interrupts: 378 * - Software vector handler 379 * - HW vector interrupt 380 */ 381 RegisterBiosInt32(BIOS_KBD_INTERRUPT, BiosKeyboardService); 382 EnableHwIRQ(1, BiosKeyboardIrq); 383 } 384 385 /* EOF */ 386