ntdll: Use a separate memory allocation for the kernel stack.
[wine.git] / dlls / krnl386.exe16 / interrupts.c
blob23c12f2a244fbbc0662e7af4187c08c3f5f37048
1 /*
2 * Interrupt emulation
4 * Copyright 2002 Jukka Heinonen
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include <stdio.h>
23 #include "wine/winbase16.h"
24 #include "kernel16_private.h"
25 #include "dosexe.h"
26 #include "winternl.h"
27 #include "wine/debug.h"
29 WINE_DEFAULT_DEBUG_CHANNEL(int);
30 WINE_DECLARE_DEBUG_CHANNEL(relay);
32 #define BCD_TO_BIN(x) ((x&15) + (x>>4)*10)
33 #define BIN_TO_BCD(x) ((x%10) + ((x/10)<<4))
35 static void WINAPI DOSVM_Int11Handler(CONTEXT*);
36 static void WINAPI DOSVM_Int12Handler(CONTEXT*);
37 static void WINAPI DOSVM_Int17Handler(CONTEXT*);
38 static void WINAPI DOSVM_Int19Handler(CONTEXT*);
39 static void WINAPI DOSVM_Int1aHandler(CONTEXT*);
40 static void WINAPI DOSVM_Int20Handler(CONTEXT*);
41 static void WINAPI DOSVM_Int2aHandler(CONTEXT*);
42 static void WINAPI DOSVM_Int41Handler(CONTEXT*);
43 static void WINAPI DOSVM_Int4bHandler(CONTEXT*);
44 static void WINAPI DOSVM_Int5cHandler(CONTEXT*);
45 static void WINAPI DOSVM_DefaultHandler(CONTEXT*);
47 static FARPROC16 DOSVM_Vectors16[256];
48 static const INTPROC DOSVM_VectorsBuiltin[] =
50 /* 00 */ 0, 0, 0, 0,
51 /* 04 */ 0, 0, 0, 0,
52 /* 08 */ 0, 0, 0, 0,
53 /* 0C */ 0, 0, 0, 0,
54 /* 10 */ 0, DOSVM_Int11Handler, DOSVM_Int12Handler, 0,
55 /* 14 */ 0, DOSVM_Int15Handler, 0, DOSVM_Int17Handler,
56 /* 18 */ 0, DOSVM_Int19Handler, DOSVM_Int1aHandler, 0,
57 /* 1C */ 0, 0, 0, 0,
58 /* 20 */ DOSVM_Int20Handler, DOSVM_Int21Handler, 0, 0,
59 /* 24 */ 0, DOSVM_Int25Handler, DOSVM_Int26Handler, 0,
60 /* 28 */ 0, 0, DOSVM_Int2aHandler, 0,
61 /* 2C */ 0, 0, 0, DOSVM_Int2fHandler,
62 /* 30 */ 0, DOSVM_Int31Handler, 0, 0,
63 /* 34 */ DOSVM_Int34Handler, DOSVM_Int35Handler, DOSVM_Int36Handler, DOSVM_Int37Handler,
64 /* 38 */ DOSVM_Int38Handler, DOSVM_Int39Handler, DOSVM_Int3aHandler, DOSVM_Int3bHandler,
65 /* 3C */ DOSVM_Int3cHandler, DOSVM_Int3dHandler, DOSVM_Int3eHandler, 0,
66 /* 40 */ 0, DOSVM_Int41Handler, 0, 0,
67 /* 44 */ 0, 0, 0, 0,
68 /* 48 */ 0, 0, 0, DOSVM_Int4bHandler,
69 /* 4C */ 0, 0, 0, 0,
70 /* 50 */ 0, 0, 0, 0,
71 /* 54 */ 0, 0, 0, 0,
72 /* 58 */ 0, 0, 0, 0,
73 /* 5C */ DOSVM_Int5cHandler
78 * Sizes of real mode and protected mode interrupt stubs.
80 #define DOSVM_STUB_PM16 5
83 /**********************************************************************
84 * DOSVM_DefaultHandler
86 * Default interrupt handler. This will be used to emulate all
87 * interrupts that don't have their own interrupt handler.
89 static void WINAPI DOSVM_DefaultHandler( CONTEXT *context )
94 /**********************************************************************
95 * DOSVM_Exit
97 void DOSVM_Exit( WORD retval )
99 DWORD count;
101 ReleaseThunkLock( &count );
102 ExitThread( retval );
106 /**********************************************************************
107 * DOSVM_GetBuiltinHandler
109 * Return Wine interrupt handler procedure for a given interrupt.
111 static INTPROC DOSVM_GetBuiltinHandler( BYTE intnum )
113 if (intnum < ARRAY_SIZE(DOSVM_VectorsBuiltin)) {
114 INTPROC proc = DOSVM_VectorsBuiltin[intnum];
115 if (proc)
116 return proc;
119 WARN("int%x not implemented, returning dummy handler\n", intnum );
120 return DOSVM_DefaultHandler;
123 /* Set up the context so that we will call __wine_call_int_handler16 upon
124 * resuming execution.
126 * We can't just call the interrupt handler directly, since some code (in
127 * particular, LoadModule16) assumes that it's running on the 32-bit stack and
128 * that CURRENT_STACK16 points to the bottom of the used 16-bit stack. */
129 static void return_to_interrupt_handler( CONTEXT *context, BYTE intnum )
131 FARPROC16 addr = GetProcAddress16( GetModuleHandle16( "KERNEL" ), "__wine_call_int_handler" );
132 WORD *stack = ldt_get_ptr( context->SegSs, context->Esp );
134 *--stack = intnum;
135 *--stack = context->SegCs;
136 *--stack = context->Eip;
137 context->Esp -= 3 * sizeof(WORD);
138 context->SegCs = SELECTOROF(addr);
139 context->Eip = OFFSETOF(addr);
142 /**********************************************************************
143 * DOSVM_PushFlags
145 * This routine is used to make default int25 and int26 handlers leave the
146 * original eflags into stack. In order to do this, stack is manipulated
147 * so that it actually contains two copies of eflags, one of which is
148 * popped during return from interrupt handler.
150 static void DOSVM_PushFlags( CONTEXT *context, BOOL islong, BOOL isstub )
152 if (islong)
154 DWORD *stack = CTX_SEG_OFF_TO_LIN(context,
155 context->SegSs,
156 context->Esp);
157 context->Esp += -4; /* One item will be added to stack. */
159 if (isstub)
161 DWORD ip = stack[0];
162 DWORD cs = stack[1];
163 stack += 2; /* Pop ip and cs. */
164 *(--stack) = context->EFlags;
165 *(--stack) = cs;
166 *(--stack) = ip;
168 else
169 *(--stack) = context->EFlags;
171 else
173 WORD *stack = CTX_SEG_OFF_TO_LIN(context,
174 context->SegSs,
175 context->Esp);
176 ADD_LOWORD( context->Esp, -2 ); /* One item will be added to stack. */
178 if (isstub)
180 WORD ip = stack[0];
181 WORD cs = stack[1];
182 stack += 2; /* Pop ip and cs. */
183 *(--stack) = LOWORD(context->EFlags);
184 *(--stack) = cs;
185 *(--stack) = ip;
187 else
188 *(--stack) = LOWORD(context->EFlags);
193 /**********************************************************************
194 * DOSVM_HardwareInterruptPM
196 * Emulate call to interrupt handler in 16-bit or 32-bit protected mode.
198 * Pushes interrupt frame to stack and changes instruction
199 * pointer to interrupt handler.
201 static void DOSVM_HardwareInterruptPM( CONTEXT *context, BYTE intnum )
203 FARPROC16 addr = DOSVM_GetPMHandler16( intnum );
205 if (SELECTOROF(addr) == int16_sel)
207 TRACE( "builtin interrupt %02x has been invoked "
208 "(through vector %02x)\n",
209 OFFSETOF(addr)/DOSVM_STUB_PM16, intnum );
211 if (intnum == 0x25 || intnum == 0x26)
212 DOSVM_PushFlags( context, FALSE, FALSE );
214 return_to_interrupt_handler( context, OFFSETOF(addr) / DOSVM_STUB_PM16 );
216 else
218 TRACE( "invoking hooked interrupt %02x at %04x:%04x\n",
219 intnum, SELECTOROF(addr), OFFSETOF(addr) );
221 /* Push the flags and return address on the stack */
222 PUSH_WORD16( context, LOWORD(context->EFlags) );
223 PUSH_WORD16( context, context->SegCs );
224 PUSH_WORD16( context, LOWORD(context->Eip) );
226 /* Jump to the interrupt handler */
227 context->SegCs = HIWORD(addr);
228 context->Eip = LOWORD(addr);
233 /**********************************************************************
234 * DOSVM_EmulateInterruptPM
236 * Emulate software interrupt in 16-bit or 32-bit protected mode.
237 * Called from signal handler when intXX opcode is executed.
239 * Pushes interrupt frame to stack and changes instruction
240 * pointer to interrupt handler.
242 BOOL DOSVM_EmulateInterruptPM( CONTEXT *context, BYTE intnum )
244 TRACE_(relay)("\1Call DOS int 0x%02x ret=%04lx:%08lx\n"
245 " eax=%08lx ebx=%08lx ecx=%08lx edx=%08lx\n"
246 " esi=%08lx edi=%08lx ebp=%08lx esp=%08lx\n"
247 " ds=%04lx es=%04lx fs=%04lx gs=%04lx ss=%04lx flags=%08lx\n",
248 intnum, context->SegCs, context->Eip,
249 context->Eax, context->Ebx, context->Ecx, context->Edx,
250 context->Esi, context->Edi, context->Ebp, context->Esp,
251 context->SegDs, context->SegEs, context->SegFs, context->SegGs,
252 context->SegSs, context->EFlags );
254 DOSMEM_InitDosMemory();
256 if (context->SegCs == int16_sel)
258 /* Restore original flags stored into the stack by the caller. */
259 WORD *stack = CTX_SEG_OFF_TO_LIN(context,
260 context->SegSs, context->Esp);
261 context->EFlags = (DWORD)MAKELONG( stack[2], HIWORD(context->EFlags) );
263 if (intnum != context->Eip / DOSVM_STUB_PM16)
264 WARN( "interrupt stub has been modified "
265 "(interrupt is %02x, interrupt stub is %02lx)\n",
266 intnum, context->Eip/DOSVM_STUB_PM16 );
268 TRACE( "builtin interrupt %02x has been branched to\n", intnum );
270 if (intnum == 0x25 || intnum == 0x26)
271 DOSVM_PushFlags( context, FALSE, TRUE );
273 return_to_interrupt_handler( context, intnum );
275 else if (ldt_is_system(context->SegCs))
277 INTPROC proc;
278 if (intnum >= ARRAY_SIZE(DOSVM_VectorsBuiltin)) return FALSE;
279 if (!(proc = DOSVM_VectorsBuiltin[intnum])) return FALSE;
280 proc( context );
282 else
284 DOSVM_HardwareInterruptPM( context, intnum );
286 return TRUE;
290 /**********************************************************************
291 * DOSVM_GetPMHandler16
293 * Return the protected mode interrupt vector for a given interrupt.
295 FARPROC16 DOSVM_GetPMHandler16( BYTE intnum )
297 TDB *pTask;
298 FARPROC16 proc = 0;
300 pTask = GlobalLock16(GetCurrentTask());
301 if (pTask)
303 switch( intnum )
305 case 0x00:
306 proc = pTask->int0;
307 break;
308 case 0x02:
309 proc = pTask->int2;
310 break;
311 case 0x04:
312 proc = pTask->int4;
313 break;
314 case 0x06:
315 proc = pTask->int6;
316 break;
317 case 0x07:
318 proc = pTask->int7;
319 break;
320 case 0x3e:
321 proc = pTask->int3e;
322 break;
323 case 0x75:
324 proc = pTask->int75;
325 break;
327 if( proc )
328 return proc;
330 if (!DOSVM_Vectors16[intnum])
332 proc = (FARPROC16)MAKESEGPTR( int16_sel, DOSVM_STUB_PM16 * intnum );
333 DOSVM_Vectors16[intnum] = proc;
335 return DOSVM_Vectors16[intnum];
339 /**********************************************************************
340 * DOSVM_SetPMHandler16
342 * Set the protected mode interrupt handler for a given interrupt.
344 void DOSVM_SetPMHandler16( BYTE intnum, FARPROC16 handler )
346 TDB *pTask;
348 TRACE("Set protected mode interrupt vector %02x <- %04x:%04x\n",
349 intnum, HIWORD(handler), LOWORD(handler) );
351 pTask = GlobalLock16(GetCurrentTask());
352 if (!pTask)
353 return;
354 switch( intnum )
356 case 0x00:
357 pTask->int0 = handler;
358 break;
359 case 0x02:
360 pTask->int2 = handler;
361 break;
362 case 0x04:
363 pTask->int4 = handler;
364 break;
365 case 0x06:
366 pTask->int6 = handler;
367 break;
368 case 0x07:
369 pTask->int7 = handler;
370 break;
371 case 0x3e:
372 pTask->int3e = handler;
373 break;
374 case 0x75:
375 pTask->int75 = handler;
376 break;
377 default:
378 DOSVM_Vectors16[intnum] = handler;
379 break;
384 /**********************************************************************
385 * DOSVM_CallBuiltinHandler
387 * Execute Wine interrupt handler procedure.
389 static void DOSVM_CallBuiltinHandler( CONTEXT *context, BYTE intnum )
392 * FIXME: Make all builtin interrupt calls go via this routine.
393 * FIXME: Check for PM->RM interrupt reflection.
394 * FIXME: Check for RM->PM interrupt reflection.
397 INTPROC proc = DOSVM_GetBuiltinHandler( intnum );
398 proc( context );
402 /**********************************************************************
403 * __wine_call_int_handler16 (KERNEL.@)
405 void WINAPI __wine_call_int_handler16( BYTE intnum, CONTEXT *context )
407 DOSMEM_InitDosMemory();
408 DOSVM_CallBuiltinHandler( context, intnum );
412 /**********************************************************************
413 * DOSVM_Int11Handler
415 * Handler for int 11h (get equipment list).
418 * Borrowed from Ralph Brown's interrupt lists:
420 * bits 15-14: number of parallel devices
421 * bit 13: [Conv] Internal modem
422 * bit 12: reserved
423 * bits 11- 9: number of serial devices
424 * bit 8: reserved
425 * bits 7- 6: number of diskette drives minus one
426 * bits 5- 4: Initial video mode:
427 * 00b = EGA,VGA,PGA
428 * 01b = 40 x 25 color
429 * 10b = 80 x 25 color
430 * 11b = 80 x 25 mono
431 * bit 3: reserved
432 * bit 2: [PS] =1 if pointing device
433 * [non-PS] reserved
434 * bit 1: =1 if math co-processor
435 * bit 0: =1 if diskette available for boot
438 * Currently the only of these bits correctly set are:
440 * bits 15-14 } Added by William Owen Smith,
441 * bits 11-9 } wos@dcs.warwick.ac.uk
442 * bits 7-6
443 * bit 2 (always set) ( bit 2 = 4 )
444 * bit 1 } Robert 'Admiral' Coeyman
445 * All *nix systems either have a math processor or
446 * emulate one.
448 static void WINAPI DOSVM_Int11Handler( CONTEXT *context )
450 int diskdrives = 0;
451 int parallelports = 0;
452 int serialports = 0;
453 int x;
455 if (GetDriveTypeA("A:\\") == DRIVE_REMOVABLE) diskdrives++;
456 if (GetDriveTypeA("B:\\") == DRIVE_REMOVABLE) diskdrives++;
457 if (diskdrives) diskdrives--;
459 for (x=0; x < 9; x++)
461 HANDLE handle;
462 char file[10];
464 /* serial port name */
465 sprintf( file, "\\\\.\\COM%d", x+1 );
466 handle = CreateFileA( file, 0, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0 );
467 if (handle != INVALID_HANDLE_VALUE)
469 CloseHandle( handle );
470 serialports++;
473 sprintf( file, "\\\\.\\LPT%d", x+1 );
474 handle = CreateFileA( file, 0, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0 );
475 if (handle != INVALID_HANDLE_VALUE)
477 CloseHandle( handle );
478 parallelports++;
482 if (serialports > 7) /* 3 bits -- maximum value = 7 */
483 serialports = 7;
485 if (parallelports > 3) /* 2 bits -- maximum value = 3 */
486 parallelports = 3;
488 SET_AX( context,
489 (diskdrives << 6) | (serialports << 9) | (parallelports << 14) | 0x06 );
493 /**********************************************************************
494 * DOSVM_Int12Handler
496 * Handler for int 12h (get memory size).
498 static void WINAPI DOSVM_Int12Handler( CONTEXT *context )
500 SET_AX( context, 640 );
504 /**********************************************************************
505 * DOSVM_Int17Handler
507 * Handler for int 17h (printer - output character).
509 static void WINAPI DOSVM_Int17Handler( CONTEXT *context )
511 switch( AH_reg(context) )
513 case 0x00:/* Send character*/
514 FIXME("Send character not supported yet\n");
515 SET_AH( context, 0x00 );/*Timeout*/
516 break;
517 case 0x01: /* PRINTER - INITIALIZE */
518 FIXME("Initialize Printer - Not Supported\n");
519 SET_AH( context, 0x30 ); /* selected | out of paper */
520 break;
521 case 0x02: /* PRINTER - GET STATUS */
522 FIXME("Get Printer Status - Not Supported\n");
523 break;
524 default:
525 SET_AH( context, 0 ); /* time out */
526 INT_BARF( context, 0x17 );
531 /**********************************************************************
532 * DOSVM_Int19Handler
534 * Handler for int 19h (Reboot).
536 static void WINAPI DOSVM_Int19Handler( CONTEXT *context )
538 TRACE( "Attempted Reboot\n" );
539 ExitProcess(0);
543 /**********************************************************************
544 * DOSVM_Int1aHandler
546 * Handler for int 1ah.
548 static void WINAPI DOSVM_Int1aHandler( CONTEXT *context )
550 switch(AH_reg(context))
552 case 0x00: /* GET SYSTEM TIME */
554 BIOSDATA *data = DOSVM_BiosData();
555 SET_CX( context, HIWORD(data->Ticks) );
556 SET_DX( context, LOWORD(data->Ticks) );
557 SET_AL( context, 0 ); /* FIXME: midnight flag is unsupported */
558 TRACE( "GET SYSTEM TIME - ticks=%ld\n", data->Ticks );
560 break;
562 case 0x01: /* SET SYSTEM TIME */
563 FIXME( "SET SYSTEM TIME - not allowed\n" );
564 break;
566 case 0x02: /* GET REAL-TIME CLOCK TIME */
567 TRACE( "GET REAL-TIME CLOCK TIME\n" );
569 SYSTEMTIME systime;
570 GetLocalTime( &systime );
571 SET_CH( context, BIN_TO_BCD(systime.wHour) );
572 SET_CL( context, BIN_TO_BCD(systime.wMinute) );
573 SET_DH( context, BIN_TO_BCD(systime.wSecond) );
574 SET_DL( context, 0 ); /* FIXME: assume no daylight saving */
575 RESET_CFLAG(context);
577 break;
579 case 0x03: /* SET REAL-TIME CLOCK TIME */
580 FIXME( "SET REAL-TIME CLOCK TIME - not allowed\n" );
581 break;
583 case 0x04: /* GET REAL-TIME CLOCK DATE */
584 TRACE( "GET REAL-TIME CLOCK DATE\n" );
586 SYSTEMTIME systime;
587 GetLocalTime( &systime );
588 SET_CH( context, BIN_TO_BCD(systime.wYear / 100) );
589 SET_CL( context, BIN_TO_BCD(systime.wYear % 100) );
590 SET_DH( context, BIN_TO_BCD(systime.wMonth) );
591 SET_DL( context, BIN_TO_BCD(systime.wDay) );
592 RESET_CFLAG(context);
594 break;
596 case 0x05: /* SET REAL-TIME CLOCK DATE */
597 FIXME( "SET REAL-TIME CLOCK DATE - not allowed\n" );
598 break;
600 case 0x06: /* SET ALARM */
601 FIXME( "SET ALARM - unimplemented\n" );
602 break;
604 case 0x07: /* CANCEL ALARM */
605 FIXME( "CANCEL ALARM - unimplemented\n" );
606 break;
608 case 0x08: /* SET RTC ACTIVATED POWER ON MODE */
609 case 0x09: /* READ RTC ALARM TIME AND STATUS */
610 case 0x0a: /* READ SYSTEM-TIMER DAY COUNTER */
611 case 0x0b: /* SET SYSTEM-TIMER DAY COUNTER */
612 case 0x0c: /* SET RTC DATE/TIME ACTIVATED POWER-ON MODE */
613 case 0x0d: /* RESET RTC DATE/TIME ACTIVATED POWER-ON MODE */
614 case 0x0e: /* GET RTC DATE/TIME ALARM AND STATUS */
615 case 0x0f: /* INITIALIZE REAL-TIME CLOCK */
616 INT_BARF( context, 0x1a );
617 break;
619 case 0xb0:
620 if (CX_reg(context) == 0x4d52 &&
621 DX_reg(context) == 0x4349 &&
622 AL_reg(context) == 0x01)
625 * Microsoft Real-Time Compression Interface (MRCI).
626 * Ignoring this call indicates MRCI is not supported.
628 TRACE( "Microsoft Real-Time Compression Interface - not supported\n" );
630 else
632 INT_BARF(context, 0x1a);
634 break;
636 default:
637 INT_BARF( context, 0x1a );
642 /**********************************************************************
643 * DOSVM_Int20Handler
645 * Handler for int 20h.
647 static void WINAPI DOSVM_Int20Handler( CONTEXT *context )
649 DOSVM_Exit( 0 );
653 /**********************************************************************
654 * DOSVM_Int2aHandler
656 * Handler for int 2ah (network).
658 static void WINAPI DOSVM_Int2aHandler( CONTEXT *context )
660 switch(AH_reg(context))
662 case 0x00: /* NETWORK INSTALLATION CHECK */
663 break;
665 default:
666 INT_BARF( context, 0x2a );
671 /***********************************************************************
672 * DOSVM_Int41Handler
674 static void WINAPI DOSVM_Int41Handler( CONTEXT *context )
676 switch ( AX_reg(context) )
678 case 0x4f:
679 case 0x50:
680 case 0x150:
681 case 0x51:
682 case 0x52:
683 case 0x152:
684 case 0x59:
685 case 0x5a:
686 case 0x5b:
687 case 0x5c:
688 case 0x5d:
689 /* Notifies the debugger of a lot of stuff. We simply ignore it
690 for now, but some of the info might actually be useful ... */
691 break;
693 default:
694 INT_BARF( context, 0x41 );
695 break;
700 /***********************************************************************
701 * DOSVM_Int4bHandler
704 static void WINAPI DOSVM_Int4bHandler( CONTEXT *context )
706 switch(AH_reg(context))
708 case 0x81: /* Virtual DMA Spec (IBM SCSI interface) */
709 if(AL_reg(context) != 0x02) /* if not install check */
711 SET_CFLAG(context);
712 SET_AL( context, 0x0f ); /* function is not implemented */
714 break;
715 default:
716 INT_BARF(context, 0x4b);
721 /***********************************************************************
722 * DOSVM_Int5cHandler
724 * Called from NetBIOSCall16.
726 static void WINAPI DOSVM_Int5cHandler( CONTEXT *context )
728 BYTE* ptr;
729 ptr = MapSL( MAKESEGPTR(context->SegEs,BX_reg(context)) );
730 FIXME("(%p): command code %02x (ignored)\n",context, *ptr);
731 *(ptr+0x01) = 0xFB; /* NetBIOS emulator not found */
732 SET_AL( context, 0xFB );