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
25 #include "wine/winbase16.h"
26 #include "kernel16_private.h"
29 #include "wine/debug.h"
31 WINE_DEFAULT_DEBUG_CHANNEL(int);
32 WINE_DECLARE_DEBUG_CHANNEL(relay
);
34 #define BCD_TO_BIN(x) ((x&15) + (x>>4)*10)
35 #define BIN_TO_BCD(x) ((x%10) + ((x/10)<<4))
37 static void WINAPI
DOSVM_Int11Handler(CONTEXT
*);
38 static void WINAPI
DOSVM_Int12Handler(CONTEXT
*);
39 static void WINAPI
DOSVM_Int17Handler(CONTEXT
*);
40 static void WINAPI
DOSVM_Int19Handler(CONTEXT
*);
41 static void WINAPI
DOSVM_Int1aHandler(CONTEXT
*);
42 static void WINAPI
DOSVM_Int20Handler(CONTEXT
*);
43 static void WINAPI
DOSVM_Int2aHandler(CONTEXT
*);
44 static void WINAPI
DOSVM_Int41Handler(CONTEXT
*);
45 static void WINAPI
DOSVM_Int4bHandler(CONTEXT
*);
46 static void WINAPI
DOSVM_Int5cHandler(CONTEXT
*);
47 static void WINAPI
DOSVM_DefaultHandler(CONTEXT
*);
49 static FARPROC16 DOSVM_Vectors16
[256];
50 static const INTPROC DOSVM_VectorsBuiltin
[] =
56 /* 10 */ 0, DOSVM_Int11Handler
, DOSVM_Int12Handler
, DOSVM_Int13Handler
,
57 /* 14 */ 0, DOSVM_Int15Handler
, 0, DOSVM_Int17Handler
,
58 /* 18 */ 0, DOSVM_Int19Handler
, DOSVM_Int1aHandler
, 0,
60 /* 20 */ DOSVM_Int20Handler
, DOSVM_Int21Handler
, 0, 0,
61 /* 24 */ 0, DOSVM_Int25Handler
, DOSVM_Int26Handler
, 0,
62 /* 28 */ 0, 0, DOSVM_Int2aHandler
, 0,
63 /* 2C */ 0, 0, 0, DOSVM_Int2fHandler
,
64 /* 30 */ 0, DOSVM_Int31Handler
, 0, 0,
65 /* 34 */ DOSVM_Int34Handler
, DOSVM_Int35Handler
, DOSVM_Int36Handler
, DOSVM_Int37Handler
,
66 /* 38 */ DOSVM_Int38Handler
, DOSVM_Int39Handler
, DOSVM_Int3aHandler
, DOSVM_Int3bHandler
,
67 /* 3C */ DOSVM_Int3cHandler
, DOSVM_Int3dHandler
, DOSVM_Int3eHandler
, 0,
68 /* 40 */ 0, DOSVM_Int41Handler
, 0, 0,
70 /* 48 */ 0, 0, 0, DOSVM_Int4bHandler
,
75 /* 5C */ DOSVM_Int5cHandler
, 0, 0, 0,
77 /* 64 */ 0, 0, 0, DOSVM_Int67Handler
,
78 /* 68 */ DOSVM_DefaultHandler
83 * Sizes of real mode and protected mode interrupt stubs.
85 #define DOSVM_STUB_PM16 5
88 /**********************************************************************
89 * DOSVM_DefaultHandler
91 * Default interrupt handler. This will be used to emulate all
92 * interrupts that don't have their own interrupt handler.
94 static void WINAPI
DOSVM_DefaultHandler( CONTEXT
*context
)
99 /**********************************************************************
100 * DOSVM_GetBuiltinHandler
102 * Return Wine interrupt handler procedure for a given interrupt.
104 static INTPROC
DOSVM_GetBuiltinHandler( BYTE intnum
)
106 if (intnum
< sizeof(DOSVM_VectorsBuiltin
)/sizeof(INTPROC
)) {
107 INTPROC proc
= DOSVM_VectorsBuiltin
[intnum
];
112 WARN("int%x not implemented, returning dummy handler\n", intnum
);
113 return DOSVM_DefaultHandler
;
117 /**********************************************************************
120 * Simple DOSRELAY that interprets its argument as INTPROC and calls it.
122 static void DOSVM_IntProcRelay( CONTEXT
*context
, LPVOID data
)
124 INTPROC proc
= (INTPROC
)data
;
129 /**********************************************************************
132 * This routine is used to make default int25 and int26 handlers leave the
133 * original eflags into stack. In order to do this, stack is manipulated
134 * so that it actually contains two copies of eflags, one of which is
135 * popped during return from interrupt handler.
137 static void DOSVM_PushFlags( CONTEXT
*context
, BOOL islong
, BOOL isstub
)
141 DWORD
*stack
= CTX_SEG_OFF_TO_LIN(context
,
144 context
->Esp
+= -4; /* One item will be added to stack. */
150 stack
+= 2; /* Pop ip and cs. */
151 *(--stack
) = context
->EFlags
;
156 *(--stack
) = context
->EFlags
;
160 WORD
*stack
= CTX_SEG_OFF_TO_LIN(context
,
163 ADD_LOWORD( context
->Esp
, -2 ); /* One item will be added to stack. */
169 stack
+= 2; /* Pop ip and cs. */
170 *(--stack
) = LOWORD(context
->EFlags
);
175 *(--stack
) = LOWORD(context
->EFlags
);
180 /**********************************************************************
181 * DOSVM_HardwareInterruptPM
183 * Emulate call to interrupt handler in 16-bit or 32-bit protected mode.
185 * Pushes interrupt frame to stack and changes instruction
186 * pointer to interrupt handler.
188 static void DOSVM_HardwareInterruptPM( CONTEXT
*context
, BYTE intnum
)
190 FARPROC16 addr
= DOSVM_GetPMHandler16( intnum
);
192 if (SELECTOROF(addr
) == DOSVM_dpmi_segments
->int16_sel
)
194 TRACE( "builtin interrupt %02x has been invoked "
195 "(through vector %02x)\n",
196 OFFSETOF(addr
)/DOSVM_STUB_PM16
, intnum
);
198 if (intnum
== 0x25 || intnum
== 0x26)
199 DOSVM_PushFlags( context
, FALSE
, FALSE
);
201 DOSVM_BuildCallFrame( context
,
203 DOSVM_GetBuiltinHandler(
204 OFFSETOF(addr
)/DOSVM_STUB_PM16
) );
208 TRACE( "invoking hooked interrupt %02x at %04x:%04x\n",
209 intnum
, SELECTOROF(addr
), OFFSETOF(addr
) );
211 /* Push the flags and return address on the stack */
212 PUSH_WORD16( context
, LOWORD(context
->EFlags
) );
213 PUSH_WORD16( context
, context
->SegCs
);
214 PUSH_WORD16( context
, LOWORD(context
->Eip
) );
216 /* Jump to the interrupt handler */
217 context
->SegCs
= HIWORD(addr
);
218 context
->Eip
= LOWORD(addr
);
223 /**********************************************************************
224 * DOSVM_EmulateInterruptPM
226 * Emulate software interrupt in 16-bit or 32-bit protected mode.
227 * Called from signal handler when intXX opcode is executed.
229 * Pushes interrupt frame to stack and changes instruction
230 * pointer to interrupt handler.
232 BOOL
DOSVM_EmulateInterruptPM( CONTEXT
*context
, BYTE intnum
)
234 TRACE_(relay
)("\1Call DOS int 0x%02x ret=%04x:%08x\n"
235 " eax=%08x ebx=%08x ecx=%08x edx=%08x\n"
236 " esi=%08x edi=%08x ebp=%08x esp=%08x\n"
237 " ds=%04x es=%04x fs=%04x gs=%04x ss=%04x flags=%08x\n",
238 intnum
, context
->SegCs
, context
->Eip
,
239 context
->Eax
, context
->Ebx
, context
->Ecx
, context
->Edx
,
240 context
->Esi
, context
->Edi
, context
->Ebp
, context
->Esp
,
241 context
->SegDs
, context
->SegEs
, context
->SegFs
, context
->SegGs
,
242 context
->SegSs
, context
->EFlags
);
244 DOSMEM_InitDosMemory();
246 if (context
->SegCs
== DOSVM_dpmi_segments
->relay_code_sel
)
249 * This must not be called using DOSVM_BuildCallFrame.
251 DOSVM_RelayHandler( context
);
253 else if (context
->SegCs
== DOSVM_dpmi_segments
->int16_sel
)
255 /* Restore original flags stored into the stack by the caller. */
256 WORD
*stack
= CTX_SEG_OFF_TO_LIN(context
,
257 context
->SegSs
, context
->Esp
);
258 context
->EFlags
= (DWORD
)MAKELONG( stack
[2], HIWORD(context
->EFlags
) );
260 if (intnum
!= context
->Eip
/ DOSVM_STUB_PM16
)
261 WARN( "interrupt stub has been modified "
262 "(interrupt is %02x, interrupt stub is %02x)\n",
263 intnum
, context
->Eip
/DOSVM_STUB_PM16
);
265 TRACE( "builtin interrupt %02x has been branched to\n", intnum
);
267 if (intnum
== 0x25 || intnum
== 0x26)
268 DOSVM_PushFlags( context
, FALSE
, TRUE
);
270 DOSVM_BuildCallFrame( context
,
272 DOSVM_GetBuiltinHandler(intnum
) );
274 else if (wine_ldt_is_system(context
->SegCs
))
277 if (intnum
>= sizeof(DOSVM_VectorsBuiltin
)/sizeof(INTPROC
)) return FALSE
;
278 if (!(proc
= DOSVM_VectorsBuiltin
[intnum
])) return FALSE
;
283 DOSVM_HardwareInterruptPM( context
, intnum
);
289 /**********************************************************************
290 * DOSVM_GetPMHandler16
292 * Return the protected mode interrupt vector for a given interrupt.
294 FARPROC16
DOSVM_GetPMHandler16( BYTE intnum
)
299 pTask
= GlobalLock16(GetCurrentTask());
329 if (!DOSVM_Vectors16
[intnum
])
331 proc
= (FARPROC16
)MAKESEGPTR( DOSVM_dpmi_segments
->int16_sel
,
332 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
)
348 TRACE("Set protected mode interrupt vector %02x <- %04x:%04x\n",
349 intnum
, HIWORD(handler
), LOWORD(handler
) );
351 pTask
= GlobalLock16(GetCurrentTask());
357 pTask
->int0
= handler
;
360 pTask
->int2
= handler
;
363 pTask
->int4
= handler
;
366 pTask
->int6
= handler
;
369 pTask
->int7
= handler
;
372 pTask
->int3e
= handler
;
375 pTask
->int75
= handler
;
378 DOSVM_Vectors16
[intnum
] = handler
;
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
);
402 /**********************************************************************
403 * __wine_call_int_handler (KERNEL.@)
405 void __wine_call_int_handler( CONTEXT
*context
, BYTE intnum
)
407 DOSMEM_InitDosMemory();
408 DOSVM_CallBuiltinHandler( context
, intnum
);
412 /**********************************************************************
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
423 * bits 11- 9: number of serial devices
425 * bits 7- 6: number of diskette drives minus one
426 * bits 5- 4: Initial video mode:
428 * 01b = 40 x 25 color
429 * 10b = 80 x 25 color
432 * bit 2: [PS] =1 if pointing device
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
443 * bit 2 (always set) ( bit 2 = 4 )
444 * bit 1 } Robert 'Admiral' Coeyman
445 * All *nix systems either have a math processor or
448 static void WINAPI
DOSVM_Int11Handler( CONTEXT
*context
)
451 int parallelports
= 0;
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
++)
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
);
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
);
482 if (serialports
> 7) /* 3 bits -- maximum value = 7 */
485 if (parallelports
> 3) /* 2 bits -- maximum value = 3 */
489 (diskdrives
<< 6) | (serialports
<< 9) | (parallelports
<< 14) | 0x06 );
493 /**********************************************************************
496 * Handler for int 12h (get memory size).
498 static void WINAPI
DOSVM_Int12Handler( CONTEXT
*context
)
500 SET_AX( context
, 640 );
504 /**********************************************************************
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*/
517 case 0x01: /* PRINTER - INITIALIZE */
518 FIXME("Initialize Printer - Not Supported\n");
519 SET_AH( context
, 0x30 ); /* selected | out of paper */
521 case 0x02: /* PRINTER - GET STATUS */
522 FIXME("Get Printer Status - Not Supported\n");
525 SET_AH( context
, 0 ); /* time out */
526 INT_BARF( context
, 0x17 );
531 /**********************************************************************
534 * Handler for int 19h (Reboot).
536 static void WINAPI
DOSVM_Int19Handler( CONTEXT
*context
)
538 TRACE( "Attempted Reboot\n" );
543 /**********************************************************************
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=%d\n", data
->Ticks
);
562 case 0x01: /* SET SYSTEM TIME */
563 FIXME( "SET SYSTEM TIME - not allowed\n" );
566 case 0x02: /* GET REAL-TIME CLOCK TIME */
567 TRACE( "GET REAL-TIME CLOCK TIME\n" );
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
);
579 case 0x03: /* SET REAL-TIME CLOCK TIME */
580 FIXME( "SET REAL-TIME CLOCK TIME - not allowed\n" );
583 case 0x04: /* GET REAL-TIME CLOCK DATE */
584 TRACE( "GET REAL-TIME CLOCK DATE\n" );
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
);
596 case 0x05: /* SET REAL-TIME CLOCK DATE */
597 FIXME( "SET REAL-TIME CLOCK DATE - not allowed\n" );
600 case 0x06: /* SET ALARM */
601 FIXME( "SET ALARM - unimplemented\n" );
604 case 0x07: /* CANCEL ALARM */
605 FIXME( "CANCEL ALARM - unimplemented\n" );
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 );
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" );
632 INT_BARF(context
, 0x1a);
637 INT_BARF( context
, 0x1a );
642 /**********************************************************************
645 * Handler for int 20h.
647 static void WINAPI
DOSVM_Int20Handler( CONTEXT
*context
)
653 /**********************************************************************
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 */
666 INT_BARF( context
, 0x2a );
671 /***********************************************************************
674 static void WINAPI
DOSVM_Int41Handler( CONTEXT
*context
)
676 switch ( AX_reg(context
) )
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 ... */
694 INT_BARF( context
, 0x41 );
700 /***********************************************************************
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 */
712 SET_AL( context
, 0x0f ); /* function is not implemented */
716 INT_BARF(context
, 0x4b);
721 /***********************************************************************
724 * Called from NetBIOSCall16.
726 static void WINAPI
DOSVM_Int5cHandler( CONTEXT
*context
)
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 );