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