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