289 lines
12 KiB
C
289 lines
12 KiB
C
|
/*
|
||
|
bimodal.c:
|
||
|
|
||
|
Example showing how to handle hardware interrupts under DOS/4GW,
|
||
|
whether the processor happens to be in real mode or protected mode
|
||
|
at the time of the interrupt.
|
||
|
|
||
|
The program installs both protected-mode and real-mode handlers for
|
||
|
interrupt 0xC (otherwise known as COM1 or IRQ4) that write either a
|
||
|
'P' to absolute address 0xB8002 or an 'R' to absolute address 0xB8000.
|
||
|
Conveniently, these locations are the first two character positions
|
||
|
in the memory map of a color display, so while the program runs
|
||
|
(at 9600 baud), you can see who is handling the interrupts as the
|
||
|
display toggles back and forth between 'P' and 'R'.
|
||
|
|
||
|
The actual data processed by both handlers is collected in a
|
||
|
single, 1024-byte buffer, which is flushed periodically (hopefully
|
||
|
before it fills up). No further processing is done on the data;
|
||
|
that's up to you. The number of interrupts processed (broken down
|
||
|
by real mode and protected mode) is printed on termination.
|
||
|
|
||
|
Note that an external DPMI host (such as Windows) will reflect real
|
||
|
mode interrupts to a protected mode handler rather than allowing the
|
||
|
real mode handler to get them; installing a real mode handler in such
|
||
|
an environment is not necessary. However, it doesn't hurt to do so,
|
||
|
so this program will handle interrupts as quickly as possible
|
||
|
whether or not a DPMI host exists.
|
||
|
|
||
|
The program is written to the DPMI (INT 31h) standard, so the code
|
||
|
is the same for both environments.
|
||
|
|
||
|
A mouse attached to COM1 makes a suitable demo (you will have to
|
||
|
load the mouse driver in order to enable the mouse hardware).
|
||
|
You can type on the keyboard as you move the mouse around; pressing
|
||
|
the Esc key ends the program. You can also transmit data from a
|
||
|
remote machine at 9600 baud; in this case, you don't need to load
|
||
|
any drivers before running the program.
|
||
|
|
||
|
Written by Dan Teven, Rational Systems Inc., 3/26/92
|
||
|
Last updated Feb. 8, 1994
|
||
|
*/
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <conio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include <dos.h>
|
||
|
|
||
|
#define D32RealSeg(P) ((((DWORD) (P)) >> 4) & 0xFFFF)
|
||
|
#define D32RealOff(P) (((DWORD) (P)) & 0xF)
|
||
|
|
||
|
typedef unsigned short WORD;
|
||
|
typedef unsigned long DWORD;
|
||
|
|
||
|
typedef struct _dpmi_callregs
|
||
|
{
|
||
|
DWORD edi, esi, ebp, esp;
|
||
|
DWORD ebx, edx, ecx, eax;
|
||
|
WORD flags;
|
||
|
WORD es, ds;
|
||
|
WORD fs, gs;
|
||
|
WORD ip, cs;
|
||
|
WORD sp, ss;
|
||
|
} DPMI_CALLREGS;
|
||
|
|
||
|
extern void __interrupt pmhandler (void);
|
||
|
extern void com_init (void);
|
||
|
|
||
|
extern void __interrupt __far rmhandler (void);
|
||
|
extern unsigned short __far com_port_low;
|
||
|
|
||
|
unsigned char com_id = 1;
|
||
|
unsigned char com_int = 0x0C;
|
||
|
unsigned short com_port = 0x3F8;
|
||
|
unsigned short lowmem_seg = 0;
|
||
|
unsigned long total_count = 0, pm_count = 0;
|
||
|
|
||
|
void *D32DosMemAlloc (DWORD size, WORD *selector)
|
||
|
{
|
||
|
union REGS r;
|
||
|
|
||
|
r.x.eax = 0x0100; /* DPMI allocate DOS memory */
|
||
|
r.x.ebx = (size + 15) >> 4; /* Number of paragraphs requested */
|
||
|
int386 (0x31, &r, &r);
|
||
|
|
||
|
if (r.x.cflag)
|
||
|
return ((DWORD) 0); /* Failed */
|
||
|
if (selector)
|
||
|
*selector = r.w.dx;
|
||
|
return (void *) ((r.x.eax & 0xFFFF) << 4);
|
||
|
}
|
||
|
|
||
|
void main (int argc, char *argv[])
|
||
|
{
|
||
|
union REGS r;
|
||
|
DPMI_CALLREGS dr;
|
||
|
struct SREGS sr;
|
||
|
char *lowp;
|
||
|
unsigned short __far *alias;
|
||
|
void far *fh;
|
||
|
WORD orig_rm_seg;
|
||
|
WORD orig_rm_off;
|
||
|
DWORD orig_pm_off;
|
||
|
WORD orig_pm_sel;
|
||
|
int c;
|
||
|
int doprot = 1;
|
||
|
int doreal = 1;
|
||
|
|
||
|
while (argc-- > 1)
|
||
|
{
|
||
|
if (argv[argc][0] != '-')
|
||
|
{
|
||
|
badarg:
|
||
|
printf ("Invalid argument '%s'\n", argv[argc]);
|
||
|
printf ("Valid options are:\n");
|
||
|
printf ("\t-p Don't install protected mode handler\n");
|
||
|
printf ("\t-r Don't install real mode handler\n");
|
||
|
printf ("\t-2 Use COM2 instead of COM1 for testing\n");
|
||
|
exit (1);
|
||
|
}
|
||
|
|
||
|
switch (argv[argc][1])
|
||
|
{
|
||
|
case 'p':
|
||
|
{
|
||
|
doprot = 0;
|
||
|
printf ("Not hooking in protected mode\n");
|
||
|
break;
|
||
|
}
|
||
|
case 'r':
|
||
|
{
|
||
|
doreal = 0;
|
||
|
printf ("Not hooking in real mode\n");
|
||
|
break;
|
||
|
}
|
||
|
case '2':
|
||
|
{
|
||
|
com_id = 2;
|
||
|
com_int = 0x0B;
|
||
|
com_port = 0x2F8;
|
||
|
printf ("Using COM2 instead of COM1 for testing\n");
|
||
|
break;
|
||
|
}
|
||
|
default:
|
||
|
goto badarg;
|
||
|
}
|
||
|
|
||
|
if (argv[argc][2])
|
||
|
goto badarg;
|
||
|
}
|
||
|
|
||
|
/* Save the starting real-mode and protected-mode handler addresses.
|
||
|
*/
|
||
|
r.x.eax = 0x0204; /* DPMI get protected mode vector */
|
||
|
r.h.bl = com_int;
|
||
|
int386 (0x31, &r, &r);
|
||
|
orig_pm_sel = (WORD) r.x.ecx;
|
||
|
orig_pm_off = r.x.edx;
|
||
|
|
||
|
r.x.eax = 0x0200; /* DPMI get real mode vector */
|
||
|
r.h.bl = com_int;
|
||
|
int386 (0x31, &r, &r);
|
||
|
orig_rm_seg = (WORD) r.x.ecx;
|
||
|
orig_rm_off = (WORD) r.x.edx;
|
||
|
|
||
|
/* Patch the code of the real mode handler so it knows which
|
||
|
com port is doing the communications. This is done before
|
||
|
the handler is copied low, to take advantage of the label
|
||
|
"com_port_low" while it's still valid.
|
||
|
*/
|
||
|
alias = &com_port_low;
|
||
|
fh = (void __far *) &com_port;
|
||
|
*(alias = MK_FP(FP_SEG(fh), FP_OFF(alias))) = com_port;
|
||
|
|
||
|
/* Allocate 128 bytes of DOS memory for the real-mode handler,
|
||
|
which must of course be less than 128 bytes long, plus 1029
|
||
|
bytes for the data. Then copy the real-mode handler to DOS
|
||
|
memory and initialize the data to zero.
|
||
|
*/
|
||
|
if (! (lowp = D32DosMemAlloc (128 + 4 + 1024 + 1, &lowmem_seg)))
|
||
|
{
|
||
|
printf ("Couldn't get low memory!\n");
|
||
|
exit (1);
|
||
|
}
|
||
|
_fmemcpy ((char __far *) lowp, (void __far *) rmhandler, 128);
|
||
|
_fmemset ((char __far *) lowp + 128, 0, 4 + 1024 + 1);
|
||
|
fh = (void __far *) pmhandler;
|
||
|
|
||
|
/* Install the new handlers. The memory touched by the protected mode
|
||
|
handler needs to be locked, in case we are running under VMM
|
||
|
or an external DPMI host.
|
||
|
*/
|
||
|
if (doprot)
|
||
|
{
|
||
|
r.x.eax = 0x0205; /* DPMI set protected mode vector */
|
||
|
r.x.ebx = (DWORD) com_int;
|
||
|
r.x.ecx = (DWORD) FP_SEG(fh);
|
||
|
r.x.edx = FP_OFF(fh);
|
||
|
int386 (0x31, &r, &r);
|
||
|
|
||
|
r.x.eax = 0x0600; /* DPMI lock linear region */
|
||
|
r.x.ebx = ((DWORD) pmhandler) >> 16;
|
||
|
r.x.ecx = ((DWORD) pmhandler) & 0xFFFF;
|
||
|
r.x.esi = 0;
|
||
|
r.x.edi = 256; /* lock 256 bytes */
|
||
|
int386 (0x31, &r, &r);
|
||
|
r.x.eax = 0x0600;
|
||
|
r.x.ebx = ((DWORD) &com_port) >> 16;
|
||
|
r.x.ecx = ((DWORD) &com_port) & 0xFFFF;
|
||
|
r.x.esi = 0;
|
||
|
r.x.edi = sizeof(WORD); /* lock 2 bytes */
|
||
|
int386 (0x31, &r, &r);
|
||
|
}
|
||
|
if (doreal)
|
||
|
{
|
||
|
r.x.eax = 0x0201; /* DPMI set real mode vector */
|
||
|
r.x.ebx = (DWORD) com_int;
|
||
|
r.x.ecx = D32RealSeg(lowp); /* CX:DX == real mode &handler */
|
||
|
r.x.edx = D32RealOff(lowp);
|
||
|
int386 (0x31, &r, &r);
|
||
|
}
|
||
|
|
||
|
/* Initialize COM port.
|
||
|
*/
|
||
|
com_init ();
|
||
|
|
||
|
puts ("Move the mouse or transmit data; press ESC to cancel\n");
|
||
|
|
||
|
/* Wait for the ESC key to be pressed. This loop has a good mix of
|
||
|
time spent in real mode and protected mode, so the upper left
|
||
|
hand corner of your color screen will toggle between 'R' and 'P'.
|
||
|
*/
|
||
|
while (1)
|
||
|
{
|
||
|
/* Explicitly go down to real mode to ask if a key is ready.
|
||
|
(The kbhit() call is simpler to code, but extra transfers to
|
||
|
real mode may be optimized out by the DOS extender.)
|
||
|
*/
|
||
|
r.x.eax = 0x300; /* DPMI signal real mode interrupt */
|
||
|
r.x.ebx = 0x16;
|
||
|
r.x.ecx = 0;
|
||
|
fh = (void far *) &dr;
|
||
|
_fmemset (fh, 0, sizeof(DPMI_CALLREGS));
|
||
|
dr.eax = 0x0100;
|
||
|
sr.ds = 0;
|
||
|
sr.es = FP_SEG(fh);
|
||
|
r.x.edi = FP_OFF(fh);
|
||
|
int386x (0x31, &r, &r, &sr);
|
||
|
|
||
|
if (! (dr.flags & 0x40)) /* Test zero flag */
|
||
|
{
|
||
|
if (((c = getch ()) & 0xff) == 27)
|
||
|
break;
|
||
|
putch (c);
|
||
|
}
|
||
|
|
||
|
/* Here we just check for an overflow, update our count of
|
||
|
interrupts, and reset the buffer. You would process the
|
||
|
data before flushing the buffer.
|
||
|
*/
|
||
|
_disable ();
|
||
|
if (*(lowp + 128 + 4 + 1024)) /* Overflow? */
|
||
|
break;
|
||
|
total_count += *((unsigned long *)(lowp + 128));
|
||
|
*((unsigned long *)(lowp + 128)) = 0;
|
||
|
_enable ();
|
||
|
}
|
||
|
|
||
|
/* Clean up.
|
||
|
*/
|
||
|
r.x.eax = 0x0201; /* DPMI set real mode vector */
|
||
|
r.x.ebx = (DWORD) com_int;
|
||
|
r.x.ecx = (DWORD) orig_rm_seg; /* CX:DX == real mode &handler */
|
||
|
r.x.edx = (DWORD) orig_rm_off;
|
||
|
int386 (0x31, &r, &r);
|
||
|
|
||
|
r.x.eax = 0x0205; /* DPMI set protected mode vector */
|
||
|
r.x.ebx = (DWORD) com_int;
|
||
|
r.x.ecx = (DWORD) orig_pm_sel;
|
||
|
r.x.edx = orig_pm_off;
|
||
|
int386 (0x31, &r, &r);
|
||
|
|
||
|
if (*(lowp + 128 + 4 + 1024))
|
||
|
printf ("\nOverflow! Increase data receive buffer size or check the\nbuffer more often.\n");
|
||
|
else
|
||
|
printf ("\n%lu interrupts processed (%lu in protected mode, %lu real mode).\n",
|
||
|
total_count, pm_count, total_count - pm_count);
|
||
|
}
|