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