[NTVDM]
[reactos.git] / reactos / subsystems / ntvdm / bios / bios32 / bios32.c
1 /*
2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: bios32.c
5 * PURPOSE: VDM 32-bit BIOS
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #define NDEBUG
12
13 #include "emulator.h"
14 #include "callback.h"
15 #include "bop.h"
16
17 #include "../bios.h"
18 #include "../rom.h"
19 #include "bios32.h"
20 #include "bios32p.h"
21 #include "kbdbios32.h"
22 #include "vidbios32.h"
23 #include "moubios32.h"
24
25 #include "io.h"
26 #include "hardware/cmos.h"
27 #include "hardware/pic.h"
28 #include "hardware/timer.h"
29
30 /* PRIVATE VARIABLES **********************************************************/
31
32 CALLBACK16 BiosContext;
33
34 /* PRIVATE FUNCTIONS **********************************************************/
35
36 static VOID WINAPI BiosException(LPWORD Stack)
37 {
38 /* Get the exception number and call the emulator API */
39 BYTE ExceptionNumber = LOBYTE(Stack[STACK_INT_NUM]);
40 EmulatorException(ExceptionNumber, Stack);
41 }
42
43 static VOID WINAPI BiosMiscService(LPWORD Stack)
44 {
45 switch (getAH())
46 {
47 /* Wait */
48 case 0x86:
49 {
50 /*
51 * Interval in microseconds in CX:DX
52 * See Ralf Brown: http://www.ctyme.com/intr/rb-1525.htm
53 * for more information.
54 */
55 Sleep(MAKELONG(getDX(), getCX()));
56
57 /* Clear CF */
58 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
59
60 break;
61 }
62
63 /* Copy Extended Memory */
64 case 0x87:
65 {
66 DWORD Count = (DWORD)getCX() * 2;
67 PFAST486_GDT_ENTRY Gdt = (PFAST486_GDT_ENTRY)SEG_OFF_TO_PTR(getES(), getSI());
68 DWORD SourceBase = Gdt[2].Base + (Gdt[2].BaseMid << 16) + (Gdt[2].BaseHigh << 24);
69 DWORD SourceLimit = Gdt[2].Limit + (Gdt[2].LimitHigh << 16);
70 DWORD DestBase = Gdt[3].Base + (Gdt[3].BaseMid << 16) + (Gdt[3].BaseHigh << 24);
71 DWORD DestLimit = Gdt[3].Limit + (Gdt[3].LimitHigh << 16);
72
73 /* Check for flags */
74 if (Gdt[2].Granularity) SourceLimit = (SourceLimit << 12) | 0xFFF;
75 if (Gdt[3].Granularity) DestLimit = (DestLimit << 12) | 0xFFF;
76
77 if ((Count > SourceLimit) || (Count > DestLimit))
78 {
79 setAX(0x80);
80 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
81
82 break;
83 }
84
85 /* Copy */
86 RtlMoveMemory((PVOID)((ULONG_PTR)BaseAddress + DestBase),
87 (PVOID)((ULONG_PTR)BaseAddress + SourceBase),
88 Count);
89
90 setAX(ERROR_SUCCESS);
91 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
92 break;
93 }
94
95 /* Get Extended Memory Size */
96 case 0x88:
97 {
98 UCHAR Low, High;
99
100 /*
101 * Return the (usable) extended memory (after 1 MB)
102 * size in kB from CMOS.
103 */
104 IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_ACTUAL_EXT_MEMORY_LOW);
105 Low = IOReadB(CMOS_DATA_PORT);
106 IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_ACTUAL_EXT_MEMORY_HIGH);
107 High = IOReadB(CMOS_DATA_PORT);
108 setAX(MAKEWORD(Low, High));
109
110 /* Clear CF */
111 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
112
113 break;
114 }
115
116 /* Get Configuration */
117 case 0xC0:
118 {
119 /* Return the BIOS ROM Configuration Table address in ES:BX */
120 // The BCT is found at F000:E6F5 for 100% compatible BIOSes.
121 setES(BIOS_SEGMENT);
122 setBX(0xE6F5);
123
124 /* Call successful; clear CF */
125 setAH(0x00);
126 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
127
128 break;
129 }
130
131 case 0xC1:
132 case 0xC2:
133 {
134 DPRINT1("INT 15h, AH = 0x%02X must be implemented in order to support vendor mouse drivers\n");
135 break;
136 }
137
138 default:
139 {
140 DPRINT1("BIOS Function INT 15h, AH = 0x%02X NOT IMPLEMENTED\n",
141 getAH());
142 }
143 }
144 }
145
146 static VOID WINAPI BiosTimeService(LPWORD Stack)
147 {
148 switch (getAH())
149 {
150 case 0x00:
151 {
152 /* Set AL to 1 if midnight had passed, 0 otherwise */
153 setAL(Bda->MidnightPassed ? 0x01 : 0x00);
154
155 /* Return the tick count in CX:DX */
156 setCX(HIWORD(Bda->TickCounter));
157 setDX(LOWORD(Bda->TickCounter));
158
159 /* Reset the midnight flag */
160 Bda->MidnightPassed = FALSE;
161
162 break;
163 }
164
165 case 0x01:
166 {
167 /* Set the tick count to CX:DX */
168 Bda->TickCounter = MAKELONG(getDX(), getCX());
169
170 /* Reset the midnight flag */
171 Bda->MidnightPassed = FALSE;
172
173 break;
174 }
175
176 default:
177 {
178 DPRINT1("BIOS Function INT 1Ah, AH = 0x%02X NOT IMPLEMENTED\n",
179 getAH());
180 }
181 }
182 }
183
184 static VOID WINAPI BiosSystemTimerInterrupt(LPWORD Stack)
185 {
186 /* Increase the system tick count */
187 Bda->TickCounter++;
188 }
189
190
191 // From SeaBIOS
192 static VOID PicSetIRQMask(USHORT off, USHORT on)
193 {
194 UCHAR pic1off = off, pic1on = on, pic2off = off>>8, pic2on = on>>8;
195 IOWriteB(PIC_MASTER_DATA, (IOReadB(PIC_MASTER_DATA) & ~pic1off) | pic1on);
196 IOWriteB(PIC_SLAVE_DATA , (IOReadB(PIC_SLAVE_DATA ) & ~pic2off) | pic2on);
197 }
198
199 // From SeaBIOS
200 VOID EnableHwIRQ(UCHAR hwirq, EMULATOR_INT32_PROC func)
201 {
202 UCHAR vector;
203
204 PicSetIRQMask(1 << hwirq, 0);
205 if (hwirq < 8)
206 vector = BIOS_PIC_MASTER_INT + hwirq;
207 else
208 vector = BIOS_PIC_SLAVE_INT + hwirq - 8;
209
210 RegisterBiosInt32(vector, func);
211 }
212
213
214 VOID PicIRQComplete(LPWORD Stack)
215 {
216 /* Get the interrupt number */
217 BYTE IntNum = LOBYTE(Stack[STACK_INT_NUM]);
218
219 /*
220 * If this was a PIC IRQ, send an End-of-Interrupt to the PIC.
221 */
222
223 if (IntNum >= BIOS_PIC_MASTER_INT && IntNum < BIOS_PIC_MASTER_INT + 8)
224 {
225 /* It was an IRQ from the master PIC */
226 IOWriteB(PIC_MASTER_CMD, PIC_OCW2_EOI);
227 }
228 else if (IntNum >= BIOS_PIC_SLAVE_INT && IntNum < BIOS_PIC_SLAVE_INT + 8)
229 {
230 /* It was an IRQ from the slave PIC */
231 IOWriteB(PIC_SLAVE_CMD , PIC_OCW2_EOI);
232 IOWriteB(PIC_MASTER_CMD, PIC_OCW2_EOI);
233 }
234 }
235
236 static VOID WINAPI BiosHandleMasterPicIRQ(LPWORD Stack)
237 {
238 BYTE IrqNumber;
239
240 IOWriteB(PIC_MASTER_CMD, PIC_OCW3_READ_ISR /* == 0x0B */);
241 IrqNumber = IOReadB(PIC_MASTER_CMD);
242
243 DPRINT("Master - IrqNumber = 0x%x\n", IrqNumber);
244
245 PicIRQComplete(Stack);
246 }
247
248 static VOID WINAPI BiosHandleSlavePicIRQ(LPWORD Stack)
249 {
250 BYTE IrqNumber;
251
252 IOWriteB(PIC_SLAVE_CMD, PIC_OCW3_READ_ISR /* == 0x0B */);
253 IrqNumber = IOReadB(PIC_SLAVE_CMD);
254
255 DPRINT("Slave - IrqNumber = 0x%x\n", IrqNumber);
256
257 PicIRQComplete(Stack);
258 }
259
260 // Timer IRQ 0
261 static VOID WINAPI BiosTimerIrq(LPWORD Stack)
262 {
263 /*
264 * Perform the system timer interrupt.
265 *
266 * Do not call directly BiosSystemTimerInterrupt(Stack);
267 * because some programs may hook only BIOS_SYS_TIMER_INTERRUPT
268 * for their purpose...
269 */
270 /** EmulatorInterrupt(BIOS_SYS_TIMER_INTERRUPT); **/
271 Int32Call(&BiosContext, BIOS_SYS_TIMER_INTERRUPT);
272 PicIRQComplete(Stack);
273 }
274
275
276 static VOID BiosHwSetup(VOID)
277 {
278 /* Initialize the master and the slave PICs (cascade mode) */
279 IOWriteB(PIC_MASTER_CMD, PIC_ICW1 | PIC_ICW1_ICW4);
280 IOWriteB(PIC_SLAVE_CMD , PIC_ICW1 | PIC_ICW1_ICW4);
281
282 /*
283 * Set the interrupt vector offsets for each PIC
284 * (base IRQs: 0x08-0x0F for IRQ 0-7, 0x70-0x77 for IRQ 8-15)
285 */
286 IOWriteB(PIC_MASTER_DATA, BIOS_PIC_MASTER_INT);
287 IOWriteB(PIC_SLAVE_DATA , BIOS_PIC_SLAVE_INT );
288
289 /* Tell the master PIC that there is a slave PIC at IRQ 2 */
290 IOWriteB(PIC_MASTER_DATA, 1 << 2);
291 /* Tell the slave PIC its cascade identity */
292 IOWriteB(PIC_SLAVE_DATA , 2);
293
294 /* Make sure both PICs are in 8086 mode */
295 IOWriteB(PIC_MASTER_DATA, PIC_ICW4_8086);
296 IOWriteB(PIC_SLAVE_DATA , PIC_ICW4_8086);
297
298 /* Clear the masks for both PICs */
299 // IOWriteB(PIC_MASTER_DATA, 0x00);
300 // IOWriteB(PIC_SLAVE_DATA , 0x00);
301 /* Disable all IRQs */
302 IOWriteB(PIC_MASTER_DATA, 0xFF);
303 IOWriteB(PIC_SLAVE_DATA , 0xFF);
304
305
306 /* Initialize PIT Counter 0 */
307 IOWriteB(PIT_COMMAND_PORT, 0x34);
308 IOWriteB(PIT_DATA_PORT(0), 0x00);
309 IOWriteB(PIT_DATA_PORT(0), 0x00);
310
311 /* Initialize PIT Counter 1 */
312 IOWriteB(PIT_COMMAND_PORT, 0x74);
313 IOWriteB(PIT_DATA_PORT(1), 0x00);
314 IOWriteB(PIT_DATA_PORT(1), 0x00);
315
316 /* Initialize PIT Counter 2 */
317 IOWriteB(PIT_COMMAND_PORT, 0xB4);
318 IOWriteB(PIT_DATA_PORT(2), 0x00);
319 IOWriteB(PIT_DATA_PORT(2), 0x00);
320
321 EnableHwIRQ(0, BiosTimerIrq);
322 }
323
324 static VOID InitializeBiosInt32(VOID)
325 {
326 USHORT i;
327
328 /* Initialize the callback context */
329 InitializeContext(&BiosContext, BIOS_SEGMENT, 0x0000);
330
331 /* Register the default BIOS 32-bit Interrupts */
332 for (i = 0x00; i <= 0xFF; i++)
333 {
334 RegisterBiosInt32(i, NULL);
335 }
336
337 /* Initialize the exception vector interrupts to a default Exception handler */
338 for (i = 0; i < 8; i++)
339 RegisterBiosInt32(i, BiosException);
340
341 /* Initialize HW vector interrupts to a default HW handler */
342 for (i = BIOS_PIC_MASTER_INT; i < BIOS_PIC_MASTER_INT + 8; i++)
343 RegisterBiosInt32(i, BiosHandleMasterPicIRQ);
344 for (i = BIOS_PIC_SLAVE_INT ; i < BIOS_PIC_SLAVE_INT + 8; i++)
345 RegisterBiosInt32(i, BiosHandleSlavePicIRQ);
346
347 /* Initialize software vector handlers */
348 RegisterBiosInt32(BIOS_EQUIPMENT_INTERRUPT, BiosEquipmentService );
349 RegisterBiosInt32(BIOS_MEMORY_SIZE , BiosGetMemorySize );
350 RegisterBiosInt32(BIOS_MISC_INTERRUPT , BiosMiscService );
351 RegisterBiosInt32(BIOS_TIME_INTERRUPT , BiosTimeService );
352 RegisterBiosInt32(BIOS_SYS_TIMER_INTERRUPT, BiosSystemTimerInterrupt);
353
354 /* Some interrupts are in fact addresses to tables */
355 ((PULONG)BaseAddress)[0x1E] = (ULONG)NULL;
356 ((PULONG)BaseAddress)[0x41] = (ULONG)NULL;
357 ((PULONG)BaseAddress)[0x46] = (ULONG)NULL;
358 ((PULONG)BaseAddress)[0x48] = (ULONG)NULL;
359 ((PULONG)BaseAddress)[0x49] = (ULONG)NULL;
360 }
361
362 static VOID InitializeBiosInfo(VOID)
363 {
364 Bct->Length = sizeof(*Bct);
365 Bct->Model = 0xFC; // PC-AT; see http://www.ctyme.com/intr/rb-1594.htm#Table515
366 Bct->SubModel = 0x00;
367 Bct->BiosRevision = 0x01;
368 Bct->BiosFeature[0] = 0x64; // At the moment we don't support "INT 15/AH=4Fh called upon INT 09h" nor "wait for external event (INT 15/AH=41h) supported"; see http://www.ctyme.com/intr/rb-1594.htm#Table510
369 Bct->BiosFeature[1] = 0x00; // We don't support anything from here; see http://www.ctyme.com/intr/rb-1594.htm#Table511
370 Bct->BiosFeature[2] = 0x00;
371 Bct->BiosFeature[3] = 0x00;
372 Bct->BiosFeature[4] = 0x00;
373 }
374
375 static VOID InitializeBiosData(VOID)
376 {
377 UCHAR Low, High;
378
379 /* Initialize the BDA contents */
380 Bda->EquipmentList = BIOS_EQUIPMENT_LIST;
381
382 /*
383 * Retrieve the conventional memory size
384 * in kB from CMOS, typically 640 kB.
385 */
386 IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_BASE_MEMORY_LOW);
387 Low = IOReadB(CMOS_DATA_PORT);
388 IOWriteB(CMOS_ADDRESS_PORT, CMOS_REG_BASE_MEMORY_HIGH);
389 High = IOReadB(CMOS_DATA_PORT);
390 Bda->MemorySize = MAKEWORD(Low, High);
391 }
392
393 /* PUBLIC FUNCTIONS ***********************************************************/
394
395 /*
396 * The BIOS POST (Power On-Self Test)
397 */
398 BOOLEAN Bios32Initialize(VOID)
399 {
400 BOOLEAN Success;
401
402 /* Initialize the stack */
403 // That's what says IBM... (stack at 30:00FF going downwards)
404 // setSS(0x0000);
405 // setSP(0x0400);
406 setSS(0x0050); // Stack at 50:0400, going downwards
407 setSP(0x0400);
408
409 /* Set data segment */
410 setDS(BDA_SEGMENT);
411
412 /* Initialize the BDA and the BIOS ROM Information */
413 InitializeBiosData();
414 InitializeBiosInfo();
415
416 /* Register the BIOS 32-bit Interrupts */
417 InitializeBiosInt32();
418
419 /* Initialize platform hardware (PIC/PIT chips, ...) */
420 BiosHwSetup();
421
422 /* Initialize the Keyboard, Video and Mouse BIOS */
423 if (!KbdBios32Initialize() || !VidBios32Initialize() || !MouseBios32Initialize())
424 return FALSE;
425
426 ///////////// MUST BE DONE AFTER IVT INITIALIZATION !! /////////////////////
427
428 /* Load some ROMs */
429 Success = LoadRom("boot.bin", (PVOID)0xE0000, NULL);
430 DPRINT1("Test ROM loading %s ; GetLastError() = %u\n", Success ? "succeeded" : "failed", GetLastError());
431
432 SearchAndInitRoms(&BiosContext);
433
434 /* We are done */
435 return TRUE;
436 }
437
438 VOID Bios32Cleanup(VOID)
439 {
440 MouseBios32Cleanup();
441 VidBios32Cleanup();
442 KbdBios32Cleanup();
443 }
444
445 /* EOF */