0d91b7a5d867554bc9182c2b219de6de001055a9
2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
9 /* INCLUDES *******************************************************************/
17 #include "hardware/pic.h"
18 #include "hardware/ps2.h"
19 #include "hardware/timer.h"
22 #include "registers.h"
24 /* PRIVATE VARIABLES **********************************************************/
27 static BYTE BiosKeyboardMap
[256];
29 /* PRIVATE FUNCTIONS **********************************************************/
31 static BOOLEAN
BiosKbdBufferPush(WORD Data
)
33 /* Get the location of the element after the tail */
34 WORD NextElement
= Bda
->KeybdBufferTail
+ sizeof(WORD
);
36 /* Wrap it around if it's at or beyond the end */
37 if (NextElement
>= Bda
->KeybdBufferEnd
) NextElement
= Bda
->KeybdBufferStart
;
39 /* If it's full, fail */
40 if (NextElement
== Bda
->KeybdBufferHead
) return FALSE
;
42 /* Put the value in the queue */
43 *((LPWORD
)((ULONG_PTR
)Bda
+ Bda
->KeybdBufferTail
)) = Data
;
44 Bda
->KeybdBufferTail
+= sizeof(WORD
);
46 /* Check if we are at, or have passed, the end of the buffer */
47 if (Bda
->KeybdBufferTail
>= Bda
->KeybdBufferEnd
)
49 /* Return it to the beginning */
50 Bda
->KeybdBufferTail
= Bda
->KeybdBufferStart
;
57 static BOOLEAN
BiosKbdBufferTop(LPWORD Data
)
59 /* If it's empty, fail */
60 if (Bda
->KeybdBufferHead
== Bda
->KeybdBufferTail
) return FALSE
;
62 /* Otherwise, get the value and return success */
63 *Data
= *((LPWORD
)((ULONG_PTR
)Bda
+ Bda
->KeybdBufferHead
));
68 static BOOLEAN
BiosKbdBufferPop(VOID
)
70 /* If it's empty, fail */
71 if (Bda
->KeybdBufferHead
== Bda
->KeybdBufferTail
) return FALSE
;
73 /* Remove the value from the queue */
74 Bda
->KeybdBufferHead
+= sizeof(WORD
);
76 /* Check if we are at, or have passed, the end of the buffer */
77 if (Bda
->KeybdBufferHead
>= Bda
->KeybdBufferEnd
)
79 /* Return it to the beginning */
80 Bda
->KeybdBufferHead
= Bda
->KeybdBufferStart
;
87 static VOID WINAPI
BiosEquipmentService(LPWORD Stack
)
89 /* Return the equipment list */
90 setAX(Bda
->EquipmentList
);
93 static VOID WINAPI
BiosGetMemorySize(LPWORD Stack
)
95 /* Return the conventional memory size in kB, typically 640 kB */
96 setAX(Bda
->MemorySize
);
99 static VOID WINAPI
BiosMiscService(LPWORD Stack
)
103 /* Copy Extended Memory */
106 DWORD Count
= (DWORD
)getCX() * 2;
107 PFAST486_GDT_ENTRY Gdt
= (PFAST486_GDT_ENTRY
)SEG_OFF_TO_PTR(getES(), getSI());
108 DWORD SourceBase
= Gdt
[2].Base
+ (Gdt
[2].BaseMid
<< 16) + (Gdt
[2].BaseHigh
<< 24);
109 DWORD SourceLimit
= Gdt
[2].Limit
+ (Gdt
[2].LimitHigh
<< 16);
110 DWORD DestBase
= Gdt
[3].Base
+ (Gdt
[3].BaseMid
<< 16) + (Gdt
[3].BaseHigh
<< 24);
111 DWORD DestLimit
= Gdt
[3].Limit
+ (Gdt
[3].LimitHigh
<< 16);
113 /* Check for flags */
114 if (Gdt
[2].Granularity
) SourceLimit
= (SourceLimit
<< 12) | 0xFFF;
115 if (Gdt
[3].Granularity
) DestLimit
= (DestLimit
<< 12) | 0xFFF;
117 if ((Count
> SourceLimit
) || (Count
> DestLimit
))
120 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_CF
;
126 RtlMoveMemory((PVOID
)((ULONG_PTR
)BaseAddress
+ DestBase
),
127 (PVOID
)((ULONG_PTR
)BaseAddress
+ SourceBase
),
130 setAX(ERROR_SUCCESS
);
131 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
135 /* Get Extended Memory Size */
138 /* Return the number of KB of RAM after 1 MB */
139 setAX((MAX_ADDRESS
- 0x100000) / 1024);
142 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_CF
;
149 DPRINT1("BIOS Function INT 15h, AH = 0x%02X NOT IMPLEMENTED\n",
155 static VOID WINAPI
BiosKeyboardService(LPWORD Stack
)
159 /* Wait for keystroke and read */
161 /* Wait for extended keystroke and read */
162 case 0x10: // FIXME: Temporarily do the same as INT 16h, 00h
164 /* Read the character (and wait if necessary) */
165 setAX(BiosGetCharacter());
169 /* Get keystroke status */
171 /* Get extended keystroke status */
172 case 0x11: // FIXME: Temporarily do the same as INT 16h, 01h
174 WORD Data
= BiosPeekCharacter();
178 /* There is a character, clear ZF and return it */
179 Stack
[STACK_FLAGS
] &= ~EMULATOR_FLAG_ZF
;
184 /* No character, set ZF */
185 Stack
[STACK_FLAGS
] |= EMULATOR_FLAG_ZF
;
191 /* Get shift status */
194 /* Return the lower byte of the keyboard shift status word */
195 setAL(LOBYTE(Bda
->KeybdShiftFlags
));
202 DPRINT1("BIOS Function INT 16h, AH = 0x04 is RESERVED\n");
209 /* Return 0 if success, 1 if failure */
210 setAL(BiosKbdBufferPush(getCX()) == FALSE
);
214 /* Get extended shift status */
218 * Be careful! The returned word is similar to Bda->KeybdShiftFlags
219 * but the high byte is organized differently:
220 * the bytes 2 and 3 of the high byte are not the same...
222 WORD KeybdShiftFlags
= (Bda
->KeybdShiftFlags
& 0xF3FF);
224 /* Return the extended keyboard shift status word */
225 setAX(KeybdShiftFlags
);
231 DPRINT1("BIOS Function INT 16h, AH = 0x%02X NOT IMPLEMENTED\n",
237 static VOID WINAPI
BiosTimeService(LPWORD Stack
)
243 /* Set AL to 1 if midnight had passed, 0 otherwise */
244 setAL(Bda
->MidnightPassed
? 0x01 : 0x00);
246 /* Return the tick count in CX:DX */
247 setCX(HIWORD(Bda
->TickCounter
));
248 setDX(LOWORD(Bda
->TickCounter
));
250 /* Reset the midnight flag */
251 Bda
->MidnightPassed
= FALSE
;
258 /* Set the tick count to CX:DX */
259 Bda
->TickCounter
= MAKELONG(getDX(), getCX());
261 /* Reset the midnight flag */
262 Bda
->MidnightPassed
= FALSE
;
269 DPRINT1("BIOS Function INT 1Ah, AH = 0x%02X NOT IMPLEMENTED\n",
275 static VOID WINAPI
BiosSystemTimerInterrupt(LPWORD Stack
)
277 /* Increase the system tick count */
281 /* PUBLIC FUNCTIONS ***********************************************************/
283 WORD
BiosPeekCharacter(VOID
)
285 WORD CharacterData
= 0;
287 /* Get the key from the queue, but don't remove it */
288 if (BiosKbdBufferTop(&CharacterData
)) return CharacterData
;
292 WORD
BiosGetCharacter(VOID
)
294 WORD CharacterData
= 0;
296 /* Check if there is a key available */
297 if (BiosKbdBufferTop(&CharacterData
))
299 /* A key was available, remove it from the queue */
304 /* No key available. Set the handler CF to repeat the BOP */
306 // CharacterData = 0xFFFF;
309 return CharacterData
;
312 BOOLEAN
BiosInitialize(HANDLE ConsoleInput
, HANDLE ConsoleOutput
)
314 /* Initialize the BDA */
315 Bda
= (PBIOS_DATA_AREA
)SEG_OFF_TO_PTR(BDA_SEGMENT
, 0);
316 Bda
->EquipmentList
= BIOS_EQUIPMENT_LIST
;
318 * Conventional memory size is 640 kB,
319 * see: http://webpages.charter.net/danrollins/techhelp/0184.HTM
320 * and see Ralf Brown: http://www.ctyme.com/intr/rb-0598.htm
321 * for more information.
323 Bda
->MemorySize
= 0x0280;
324 Bda
->KeybdBufferStart
= FIELD_OFFSET(BIOS_DATA_AREA
, KeybdBuffer
);
325 Bda
->KeybdBufferEnd
= Bda
->KeybdBufferStart
+ BIOS_KBD_BUFFER_SIZE
* sizeof(WORD
);
326 Bda
->KeybdBufferHead
= Bda
->KeybdBufferTail
= 0;
328 /* Initialize the 32-bit Interrupt system */
329 InitializeInt32(BIOS_SEGMENT
);
331 /* Register the BIOS 32-bit Interrupts */
332 RegisterInt32(BIOS_EQUIPMENT_INTERRUPT
, BiosEquipmentService
);
333 RegisterInt32(BIOS_MEMORY_SIZE
, BiosGetMemorySize
);
334 RegisterInt32(BIOS_MISC_INTERRUPT
, BiosMiscService
);
335 RegisterInt32(BIOS_KBD_INTERRUPT
, BiosKeyboardService
);
336 RegisterInt32(BIOS_TIME_INTERRUPT
, BiosTimeService
);
337 RegisterInt32(BIOS_SYS_TIMER_INTERRUPT
, BiosSystemTimerInterrupt
);
339 /* Some interrupts are in fact addresses to tables */
340 ((PDWORD
)BaseAddress
)[0x1D] = (DWORD
)NULL
;
341 ((PDWORD
)BaseAddress
)[0x1E] = (DWORD
)NULL
;
342 ((PDWORD
)BaseAddress
)[0x1F] = (DWORD
)NULL
;
344 ((PDWORD
)BaseAddress
)[0x41] = (DWORD
)NULL
;
345 ((PDWORD
)BaseAddress
)[0x43] = (DWORD
)NULL
;
346 ((PDWORD
)BaseAddress
)[0x44] = (DWORD
)NULL
;
347 ((PDWORD
)BaseAddress
)[0x46] = (DWORD
)NULL
;
348 ((PDWORD
)BaseAddress
)[0x48] = (DWORD
)NULL
;
349 ((PDWORD
)BaseAddress
)[0x49] = (DWORD
)NULL
;
351 /* Initialize the Video BIOS */
352 if (!VidBiosInitialize(ConsoleOutput
)) return FALSE
;
354 /* Set the console input mode */
355 SetConsoleMode(ConsoleInput
, ENABLE_MOUSE_INPUT
| ENABLE_PROCESSED_INPUT
);
358 PS2Initialize(ConsoleInput
);
361 * The POST (Power On-Self Test)
364 /* Initialize the PIC */
365 IOWriteB(PIC_MASTER_CMD
, PIC_ICW1
| PIC_ICW1_ICW4
);
366 IOWriteB(PIC_SLAVE_CMD
, PIC_ICW1
| PIC_ICW1_ICW4
);
368 /* Set the interrupt offsets */
369 IOWriteB(PIC_MASTER_DATA
, BIOS_PIC_MASTER_INT
);
370 IOWriteB(PIC_SLAVE_DATA
, BIOS_PIC_SLAVE_INT
);
372 /* Tell the master PIC there is a slave at IRQ 2 */
373 IOWriteB(PIC_MASTER_DATA
, 1 << 2);
374 IOWriteB(PIC_SLAVE_DATA
, 2);
376 /* Make sure the PIC is in 8086 mode */
377 IOWriteB(PIC_MASTER_DATA
, PIC_ICW4_8086
);
378 IOWriteB(PIC_SLAVE_DATA
, PIC_ICW4_8086
);
380 /* Clear the masks for both PICs */
381 IOWriteB(PIC_MASTER_DATA
, 0x00);
382 IOWriteB(PIC_SLAVE_DATA
, 0x00);
384 PitWriteCommand(0x34);
385 PitWriteData(0, 0x00);
386 PitWriteData(0, 0x00);
391 VOID
BiosCleanup(VOID
)
397 VOID
BiosHandleIrq(BYTE IrqNumber
, LPWORD Stack
)
404 /* Perform the system timer interrupt */
405 EmulatorInterrupt(BIOS_SYS_TIMER_INTERRUPT
);
412 BYTE ScanCode
, VirtualKey
;
415 /* Get the scan code and virtual key code */
416 ScanCode
= IOReadB(PS2_DATA_PORT
);
417 VirtualKey
= MapVirtualKey(ScanCode
& 0x7F, MAPVK_VSC_TO_VK
);
419 /* Check if this is a key press or release */
420 if (!(ScanCode
& (1 << 7)))
423 if (VirtualKey
== VK_NUMLOCK
||
424 VirtualKey
== VK_CAPITAL
||
425 VirtualKey
== VK_SCROLL
||
426 VirtualKey
== VK_INSERT
)
428 /* For toggle keys, toggle the lowest bit in the keyboard map */
429 BiosKeyboardMap
[VirtualKey
] ^= ~(1 << 0);
432 /* Set the highest bit */
433 BiosKeyboardMap
[VirtualKey
] |= (1 << 7);
435 /* Find out which character this is */
437 if (ToAscii(VirtualKey
, ScanCode
, BiosKeyboardMap
, &Character
, 0) == 0)
443 /* Push it onto the BIOS keyboard queue */
444 BiosKbdBufferPush(MAKEWORD(Character
, ScanCode
));
448 /* Key release, unset the highest bit */
449 BiosKeyboardMap
[VirtualKey
] &= ~(1 << 7);
452 /* Clear the keyboard flags */
453 Bda
->KeybdShiftFlags
= 0;
455 /* Set the appropriate flags based on the state */
456 if (BiosKeyboardMap
[VK_RSHIFT
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_RSHIFT
;
457 if (BiosKeyboardMap
[VK_LSHIFT
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_LSHIFT
;
458 if (BiosKeyboardMap
[VK_CONTROL
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_CTRL
;
459 if (BiosKeyboardMap
[VK_MENU
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_ALT
;
460 if (BiosKeyboardMap
[VK_SCROLL
] & (1 << 0)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_SCROLL_ON
;
461 if (BiosKeyboardMap
[VK_NUMLOCK
] & (1 << 0)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_NUMLOCK_ON
;
462 if (BiosKeyboardMap
[VK_CAPITAL
] & (1 << 0)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_CAPSLOCK_ON
;
463 if (BiosKeyboardMap
[VK_INSERT
] & (1 << 0)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_INSERT_ON
;
464 if (BiosKeyboardMap
[VK_RMENU
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_RALT
;
465 if (BiosKeyboardMap
[VK_LMENU
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_LALT
;
466 if (BiosKeyboardMap
[VK_SNAPSHOT
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_SYSRQ
;
467 if (BiosKeyboardMap
[VK_PAUSE
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_PAUSE
;
468 if (BiosKeyboardMap
[VK_SCROLL
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_SCROLL
;
469 if (BiosKeyboardMap
[VK_NUMLOCK
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_NUMLOCK
;
470 if (BiosKeyboardMap
[VK_CAPITAL
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_CAPSLOCK
;
471 if (BiosKeyboardMap
[VK_INSERT
] & (1 << 7)) Bda
->KeybdShiftFlags
|= BDA_KBDFLAG_INSERT
;
477 /* Send End-of-Interrupt to the PIC */
478 if (IrqNumber
>= 8) IOWriteB(PIC_SLAVE_CMD
, PIC_OCW2_EOI
);
479 IOWriteB(PIC_MASTER_CMD
, PIC_OCW2_EOI
);