dave@mgc.uucp (Dave Lockwood) (05/15/91)
This is the second posting - I didn't see any replies the first time :-( I'm trying to write a Windows 3 application which needs to interface to a DOS TSR which communicates via an interrupt. Everything works fine in real mode, but I get UAEs and reboots in both standard and 386 enhanced mode. The code segment that's causing the trouble: union REGS InRegs; union REGS OutRegs; struct SREGS SegRegs; HANDLE hBuffer; LPSTR lpBuffer; InRegs.h.ah = 11; InRegs.h.al = 1; InRegs.x.dx = FP_OFF(lpBuffer); hBuffer = GlobalAlloc(GHND,512L); lpBuffer = GlobalLock(hBuffer); InRegs.x.di = FP_OFF(lpBuffer); SegRegs.es = FP_SEG(lpBuffer); int86x(0x7f,&InRegs,&OutRegs,&SegRegs); I've done a lot of RTFM-ing, but to no avail (at least, the problem isn't solved). I didn't write the TSR, so it's difficult to get changes done there (I don't have the source). My suspicions are that lpBuffer above is a 'selector' (whatever that is) rather than a true, _far address, and when the TSR (assembler) uses the ES:DI register pair, some sort of protect trap takes place. Any ideas on how to solve this? Do I need to spend $500 on the DDK and write a special device driver? Please help - any comments would be appreciated; if you did respond via a posting last time, please respond again. My upstream site lost some news just about the "right" time :-(. Thanks in advance! -- Dave Lockwood dave@mgc.uucp G4CLI@GB7DCC._199.GBR.EU Head of Technology ...!uunet!mcsun!ukc!mgc!dave ----MG Computer Systems Ltd, PO Box 50, Doncaster, DN4 5AW +44-302-738770----
roger@bjcong.bj.co.uk (ROGER JAMES) (05/17/91)
In article <1991May15.133642.40@mgc.uucp> dave@mgc.UUCP (Dave Lockwood) writes: > >I'm trying to write a Windows 3 application which needs to interface to a >DOS TSR which communicates via an interrupt. Everything works fine in real >mode, but I get UAEs and reboots in both standard and 386 enhanced mode. > >My suspicions are that lpBuffer above is a 'selector' (whatever that is) >rather than a true, _far address, and when the TSR (assembler) uses the >ES:DI register pair, some sort of protect trap takes place. > Your suspiscions are correct. The TSR needs to be a passed an address that is valid in real mode (i.e it is a segment/offset rather than a selector/offset ). You can allocate a buffer that has such a valid address by using the GlobalDOSAlloc function. However you will not be able to pass this address to your TSR in a segment register. This will cause a UAE. You can pass this address in a non segment register, which is the easiest way to do it if you have access to the source of the TSR. If you dont have access to the source then _here comes the bit you dont want to hear_. You need to call your TSR via DPMI, there are various examples of how to do this knocking around on Online and Compuserve, it is not straightforward. Here comes the _next bit you dont want to hear_. If the buffer you passed to the TSR is going to be used at interrupt time (i.e. from within a hardware interrupt service routine). Then your system will crash whenever you run DOS shells from windows. There are a number of ways round this, all of which are very messy. -- roger@bj.co.uk (UK only) roger@boldon-james-limited.co.uk (Internet) ..!mcsun!ukc!pyrltd!bjcong!roger
Norbert_Unterberg@p4.f36.n245.z2.fidonet.org (Norbert Unterberg) (05/17/91)
> I'm trying to write a Windows 3 application which needs to interface to a > DOS TSR which communicates via an interrupt. Everything works fine in real > mode, but I get UAEs and reboots in both standard and 386 enhanced mode. > > The code segment that's causing the trouble: > [...] > hBuffer = GlobalAlloc(GHND,512L); > lpBuffer = GlobalLock(hBuffer); > InRegs.x.di = FP_OFF(lpBuffer); > SegRegs.es = FP_SEG(lpBuffer); > > int86x(0x7f,&InRegs,&OutRegs,&SegRegs); > > I've done a lot of RTFM-ing, but to no avail (at least, the problem > isn't > solved). I didn't write the TSR, so it's difficult to get changes done > there (I don't have the source). You obviously have read the wrong manual. Ever heard of the DPMI (Dos Protected Mode Interface)? That should solve your problems. The protected mode you must watch out for a lot of things you can ignore in real mode. Here are some of the differences: - In protected mode the processor can access more than 1MB of memory. In real mode only the 1st MByte can be reached by the processor. So if you GlobalAlloc some memory in protected mode, that memory can be anywhere, very likely somewhere above 1 MB. Hence your TSR (in real mode) can _not_ reach that memory. - In proteted mode all segment `addresses' are no longer `adresses'. All values stored in segment registers are selectors. A selector is an index in a (local or global) descriptor table (LDT/GDT), which contains the linear addresses for the segments. So a selector only identifies a segment. That si similar to the Windows memory handle. The segment can float in memory, but the selector remains valid, as long as the segment address is updated in the descriptor table. - The protected mode has its own interrupt table, which is not identical with the real mode interrupt table. So calling a (real mode) TSR from protected mode must fail, because the protected mode interrupt vector does not point to the TSR. Because these restrictions make it (nearly) impossible for a protected mode application to communicate with a real mode programm, some people from Borland, Microsoft, Intel, Lotus, Phar Lap etc. came together and developed the DPMI specification. DPMI stands for Dos Protected Mode Interface and is an API for protected mode programs that allows it to do several (dirty) things and tricks otherwise not possible in PM. A copy of that specification can freely(!) be orderd by Intel, Santa Clara. This DPMI has all the functions you need to call yout TSR. Fist you must allocate the memory you need in the real mode addressable 1 MB region. That is done with function `Allocate DOS Memory Block'. The result is a selector AND the segment address of the memory block. Then you fill in the required register values for the TSR into a data area called `real mode call structur'. When calling the real mode program, all registers will be loaded from this structure. Then you can call yout TSR via the DPMI fuction `Simulate Real Mode Interrupt'. The TSR will (hopefully) do its job and fill the memory with the expected values. And don't forget to free the DOS memory block, if it is no longer needed. Hope I could give you some clues... Norbert, Dortmund, FRG
glenn@imagen.com (glenn boozer) (05/18/91)
>This is the second posting - I didn't see any replies the first time :-( > >I'm trying to write a Windows 3 application which needs to interface to a >DOS TSR which communicates via an interrupt. Everything works fine in real >mode, but I get UAEs and reboots in both standard and 386 enhanced mode. > >The code segment that's causing the trouble: > > union REGS InRegs; > union REGS OutRegs; > struct SREGS SegRegs; > HANDLE hBuffer; > LPSTR lpBuffer; > > InRegs.h.ah = 11; > InRegs.h.al = 1; > InRegs.x.dx = FP_OFF(lpBuffer); > > hBuffer = GlobalAlloc(GHND,512L); Does not work in either Protected mode > lpBuffer = GlobalLock(hBuffer); > InRegs.x.di = FP_OFF(lpBuffer); > SegRegs.es = FP_SEG(lpBuffer); > > int86x(0x7f,&InRegs,&OutRegs,&SegRegs); > >I've done a lot of RTFM-ing, but to no avail (at least, the problem isn't >solved). I didn't write the TSR, so it's difficult to get changes done >there (I don't have the source). You shouldn't need to change the TSR. > >My suspicions are that lpBuffer above is a 'selector' (whatever that is) >rather than a true, _far address, and when the TSR (assembler) uses the >ES:DI register pair, some sort of protect trap takes place. You guessed it! > >Any ideas on how to solve this? Do I need to spend $500 on the DDK and write >a special device driver? Please help - any comments would be appreciated; >if you did respond via a posting last time, please respond again. My upstream >site lost some news just about the "right" time :-(. The DDK is not needed for this. The problem is that the TSR runs in REAL mode and the windows applications running in either protected mode use selectors[LDT] and not pointers[SEG:OFF] to access buffers. What follows I hastily pieced together from code I have working. In a nutshell: To Send a buffer address to the TSR [Send it a REAL address of Segment:Offset, not a protected mode selector] 1) DosAllocate a memory buffer [This will be a buffer in the first 640K of address space. This buffer is LOCKED and will not move. [Not recomended by Microsoft] 2) Copy the data from the buffer that is in "Windows Application space" into the DOS Buffer 3) Get the Segment and Offset of the DOS buffer and pass that to the TSR. 4) Release the DOS Buffer To use the buffer address [Segment:offset] that the TSR send you: 1) Allocate a Selector [Not recomended by Microsoft] 2) Set the selector base and length with the data returned from the TSR. [Not recomended by Microsoft] 3) Use the data 4) Return the selector. [Not recomended by Microsoft] Code fragments follow. If I left anything out, give me a note. #define FarPtr far * /* Data structure for calls to the TSR */ typedef union { char FarPtr p; /* treat as pointer */ void FarPtr pv; struct /* treat as selector/offset */ { unsigned off; unsigned sel; } w; long l; /*treat as arithmetic item */ } FPTR; /* Data structure for value returned from GlobalDosAlloc*/ typedef union { struct /* treat as selector and a paragraph */ { WORD sel; WORD par; } w; DWORD d; /*treat as DWORD */ } DOSPS; /* Windows kernel calls not in WINDOWS.H */ WORD FAR PASCAL SetSelectorBase(HANDLE hSelector, DWORD dwBase); WORD FAR PASCAL SetSelectorLimit(HANDLE hSelector, DWORD dwLimit); DWORD FAR PASCAL GlobalDosAlloc(DWORD); WORD FAR PASCAL GlobalDosFree(WORD); #define WIND386ENHANCEDMODE (dwWinMemFlags & WF_ENHANCED) #define WINDPROTECTEDMODE (dwWinMemFlags & WF_PMODE) #define WINDREALMODE (!(dwWinMemFlags & WF_PMODE)) static DWORD near dwWinMemFlags; /* Window Memory configuration Flags */ static DOSPS near lpsDosParagraphSelector; static WORD near hPhysMemHandle; /* Handle to DOS memory */ static LPSTR near lpPhysPtr; /* pointer to DOS memory */ /*************************************************************************** *Name: GetWindowsMemoryConfig * * * * Get windows memory configuration word so we can execute Real Mode and * * Protected Mode specific code. * * * *Returns: Nothing * ***************************************************************************/ void GetWindowsMemoryConfig () { dwWinMemFlags = GetWinFlags(); } /*********************************************************************** Module: GetPhysicalMemoryHandle Sets up a physical memory handle which can be used to access memory in protected mode. If the selector cannot be created, the call fails and returns NULL. returns: Memory access handle. **********************************************************************/ WORD FAR PASCAL GetPhysicalMemoryHandle() { HANDLE hSel; HANDLE hSel2; /*. create a selector for use by MakePhysicalMemoryPtr() */ /* The how of this is taken from an SR response. */ if ((hSel2 = GlobalAlloc(GMEM_FIXED,(long) 64)) != NULL) { hSel = AllocSelector(hSel2); GlobalFree(hSel2); } else { return (WORD)NULL; } /* successful: return a handle */ return hSel; } /*********************************************************************** Module: MakePhysicalMemoryPtr This routine sets up a protected mode pointer to a specified physical address. This pointer is good for 4K of memory at the specified physical segment + offset address. If NULL, an error occurred. returns: Memory access handle. **********************************************************************/ LPSTR NEAR PASCAL MakePhysicalMemoryPtr( WORD wMemHandle, /* Physical memory handle created by GetPhysicalMemoryHandle(). a valid selector is passed. */ WORD wSegment, /* Segment address to be referenced. This is a real-mode paragraph number. */ WORD wOffset /* Offset from Segment address to be referenced. */ ) { /*. set selector base from wSegment parameter */ SetSelectorBase(wMemHandle, (((LONG)wSegment)<<4) + wOffset ); /*. set limit for 4K bytes accessable */ SetSelectorLimit(wMemHandle,(long) 0x0FFF); /*. make and return a long pointer using passed wMemHandle */ return (LPSTR)MAKELONG(0, wMemHandle); } /*************************************************************************** Routine: FreePhysicalMemoryPtr Deallocates the specified physical memory pointer. This call is used to free a selector in protect mode. Once this function is run on a pointer, the physical memory handle which was used to create that pointer CANNOT BE USED AGAIN. Returns: nothing ***************************************************************************/ void FAR PASCAL FreePhysicalMemoryPtr( LPSTR lpMemPtr /* Pointer used for access to physical memory. The pointer must have been constructed by MakePhysicalMemoryPtr().*/ ) { /*. free the selector */ FreeSelector(HIWORD(lpMemPtr)); } [Main code fragment] This code handles Real, Standard, and Enhanced memory models all at the same time. /* Get Memory Model [Real/Standard/Enhanced] */ GetWindowsMemoryConfig(); /* If in protected mode, assign LOW memory for use with TSR */ if (WINDPROTECTEDMODE) { /* Assign a memory handle for physical address usage with TSR */ if(!( hPhysMemHandle = GetPhysicalMemoryHandle())) { MessageBoxOKHand ((LPSTR) "Error ??: Could not get Physical Memory for TSR"); return; } } [call tsr-calling routine n times] /* if in protected mode, return the handle we allocated */ if ( WINDPROTECTEDMODE) { FreePhysicalMemoryPtr(lpPhysPtr); } return; [TSR-Calling routine] static FPTR near fp; if( WINDPROTECTEDMODE) { /* allocate DOS memory to put data into */ lpsDosParagraphSelector.d = GlobalDosAlloc((DWORD)max( APImsg.len, 4)); if(APImsg.buffer) { lmemcpy( (LPSTR)MAKELONG(0, lpsDosParagraphSelector.w.sel), APImsg.buffer, max( APImsg.len, 4)); fp.w.sel = lpsDosParagraphSelector.w.par; fp.w.off = 0; } else { /* Null Pointer */ fp.p = 0L; } } else { /* If in real mode */ fp.p = APImsg.buffer; } dx = fp.w.sel; bx = fp.w.off; /* Call the tsr */ rc = int2f(ax, cx, si, di, dx, bx, (unsigned int far *)&di, (unsigned int far *)&si, (unsigned int far *)&cx, (unsigned int far *)&dx, (unsigned int far *)&bx); if(WINDPROTECTEDMODE { (void) GlobalDosFree(lpsDosParagraphSelector.w.sel); } if(WINDREALMODE) { fp.w.sel = dx; fp.w.off = bx; } else { lpPhysPtr = MakePhysicalMemoryPtr(hPhysMemHandle, dx /* segment addr */, bx /* offset */); fp.p = lpPhysPtr; /* use pointer lpPhysPtr */ } Glenn Boozer glenn@imagen.com QMS [PostScript is what we do best]
glenn@imagen.com (glenn boozer) (05/18/91)
dave@mgc.uucp (Dave Lockwood) writes: >This is the second posting - I didn't see any replies the first time :-( > >I'm trying to write a Windows 3 application which needs to interface to a >DOS TSR which communicates via an interrupt. Everything works fine in real >mode, but I get UAEs and reboots in both standard and 386 enhanced mode. > >The code segment that's causing the trouble: > > union REGS InRegs; > union REGS OutRegs; > struct SREGS SegRegs; > HANDLE hBuffer; > LPSTR lpBuffer; > > InRegs.h.ah = 11; > InRegs.h.al = 1; > InRegs.x.dx = FP_OFF(lpBuffer); > > hBuffer = GlobalAlloc(GHND,512L); Does not work in either Protected mode > lpBuffer = GlobalLock(hBuffer); > InRegs.x.di = FP_OFF(lpBuffer); > SegRegs.es = FP_SEG(lpBuffer); > > int86x(0x7f,&InRegs,&OutRegs,&SegRegs); > >I've done a lot of RTFM-ing, but to no avail (at least, the problem isn't >solved). I didn't write the TSR, so it's difficult to get changes done >there (I don't have the source). You shouldn't need to change the TSR. > >My suspicions are that lpBuffer above is a 'selector' (whatever that is) >rather than a true, _far address, and when the TSR (assembler) uses the >ES:DI register pair, some sort of protect trap takes place. You guessed it! > >Any ideas on how to solve this? Do I need to spend $500 on the DDK and write >a special device driver? Please help - any comments would be appreciated; >if you did respond via a posting last time, please respond again. My upstream >site lost some news just about the "right" time :-(. The DDK is not needed for this. The problem is that the TSR runs in REAL mode and the windows applications running in either protected mode use selectors[LDT] and not pointers[SEG:OFF] to access buffers. What follows I hastily pieced together from code I have working. In a nutshell: To Send a buffer address to the TSR [Send it a REAL address of Segment:Offset, not a protected mode selector] 1) DosAllocate a memory buffer [This will be a buffer in the first 640K of address space. This buffer is LOCKED and will not move. [Not recomended by Microsoft] 2) Copy the data from the buffer that is in "Windows Application space" into the DOS Buffer 3) Get the Segment and Offset of the DOS buffer and pass that to the TSR. 4) Release the DOS Buffer To use the buffer address [Segment:offset] that the TSR send you: 1) Allocate a Selector [Not recomended by Microsoft] 2) Set the selector base and length with the data returned from the TSR. [Not recomended by Microsoft] 3) Use the data 4) Return the selector. [Not recomended by Microsoft] Code fragments follow. If I left anything out, give me a note. #define FarPtr far * /* Data structure for calls to the TSR */ typedef union { char FarPtr p; /* treat as pointer */ void FarPtr pv; struct /* treat as selector/offset */ { unsigned off; unsigned sel; } w; long l; /*treat as arithmetic item */ } FPTR; /* Data structure for value returned from GlobalDosAlloc*/ typedef union { struct /* treat as selector and a paragraph */ { WORD sel; WORD par; } w; DWORD d; /*treat as DWORD */ } DOSPS; /* Windows kernel calls not in WINDOWS.H */ WORD FAR PASCAL SetSelectorBase(HANDLE hSelector, DWORD dwBase); WORD FAR PASCAL SetSelectorLimit(HANDLE hSelector, DWORD dwLimit); DWORD FAR PASCAL GlobalDosAlloc(DWORD); WORD FAR PASCAL GlobalDosFree(WORD); #define WIND386ENHANCEDMODE (dwWinMemFlags & WF_ENHANCED) #define WINDPROTECTEDMODE (dwWinMemFlags & WF_PMODE) #define WINDREALMODE (!(dwWinMemFlags & WF_PMODE)) static DWORD near dwWinMemFlags; /* Window Memory configuration Flags */ static DOSPS near lpsDosParagraphSelector; static WORD near hPhysMemHandle; /* Handle to DOS memory */ static LPSTR near lpPhysPtr; /* pointer to DOS memory */ /*************************************************************************** *Name: GetWindowsMemoryConfig * * * * Get windows memory configuration word so we can execute Real Mode and * * Protected Mode specific code. * * * *Returns: Nothing * ***************************************************************************/ void GetWindowsMemoryConfig () { dwWinMemFlags = GetWinFlags(); } /*********************************************************************** Module: GetPhysicalMemoryHandle Sets up a physical memory handle which can be used to access memory in protected mode. If the selector cannot be created, the call fails and returns NULL. returns: Memory access handle. **********************************************************************/ WORD FAR PASCAL GetPhysicalMemoryHandle() { HANDLE hSel; HANDLE hSel2; /*. create a selector for use by MakePhysicalMemoryPtr() */ /* The how of this is taken from an SR response. */ if ((hSel2 = GlobalAlloc(GMEM_FIXED,(long) 64)) != NULL) { hSel = AllocSelector(hSel2); GlobalFree(hSel2); } else { return (WORD)NULL; } /* successful: return a handle */ return hSel; } /*********************************************************************** Module: MakePhysicalMemoryPtr This routine sets up a protected mode pointer to a specified physical address. This pointer is good for 4K of memory at the specified physical segment + offset address. If NULL, an error occurred. returns: Memory access handle. **********************************************************************/ LPSTR NEAR PASCAL MakePhysicalMemoryPtr( WORD wMemHandle, /* Physical memory handle created by GetPhysicalMemoryHandle(). a valid selector is passed. */ WORD wSegment, /* Segment address to be referenced. This is a real-mode paragraph number. */ WORD wOffset /* Offset from Segment address to be referenced. */ ) { /*. set selector base from wSegment parameter */ SetSelectorBase(wMemHandle, (((LONG)wSegment)<<4) + wOffset ); /*. set limit for 4K bytes accessable */ SetSelectorLimit(wMemHandle,(long) 0x0FFF); /*. make and return a long pointer using passed wMemHandle */ return (LPSTR)MAKELONG(0, wMemHandle); } /*************************************************************************** Routine: FreePhysicalMemoryPtr Deallocates the specified physical memory pointer. This call is used to free a selector in protect mode. Once this function is run on a pointer, the physical memory handle which was used to create that pointer CANNOT BE USED AGAIN. Returns: nothing ***************************************************************************/ void FAR PASCAL FreePhysicalMemoryPtr( LPSTR lpMemPtr /* Pointer used for access to physical memory. The pointer must have been constructed by MakePhysicalMemoryPtr().*/ ) { /*. free the selector */ FreeSelector(HIWORD(lpMemPtr)); } [Main code fragment] This code handles Real, Standard, and Enhanced memory models all at the same time. /* Get Memory Model [Real/Standard/Enhanced] */ GetWindowsMemoryConfig(); /* If in protected mode, assign LOW memory for use with TSR */ if (WINDPROTECTEDMODE) { /* Assign a memory handle for physical address usage with TSR */ if(!( hPhysMemHandle = GetPhysicalMemoryHandle())) { MessageBoxOKHand ((LPSTR) "Error ??: Could not get Physical Memory for TSR"); return; } } [call tsr-calling routine n times] /* if in protected mode, return the handle we allocated */ if ( WINDPROTECTEDMODE) { FreePhysicalMemoryPtr(lpPhysPtr); } return; [TSR-Calling routine] static FPTR near fp; if( WINDPROTECTEDMODE) { /* allocate DOS memory to put data into */ lpsDosParagraphSelector.d = GlobalDosAlloc((DWORD)max( APImsg.len, 4)); if(APImsg.buffer) { lmemcpy( (LPSTR)MAKELONG(0, lpsDosParagraphSelector.w.sel), APImsg.buffer, max( APImsg.len, 4)); fp.w.sel = lpsDosParagraphSelector.w.par; fp.w.off = 0; } else { /* Null Pointer */ fp.p = 0L; } } else { /* If in real mode */ fp.p = APImsg.buffer; } dx = fp.w.sel; bx = fp.w.off; /* Call the tsr */ rc = int2f(ax, cx, si, di, dx, bx, (unsigned int far *)&di, (unsigned int far *)&si, (unsigned int far *)&cx, (unsigned int far *)&dx, (unsigned int far *)&bx); if(WINDPROTECTEDMODE { (void) GlobalDosFree(lpsDosParagraphSelector.w.sel); } if(WINDREALMODE) { fp.w.sel = dx; fp.w.off = bx; } else { lpPhysPtr = MakePhysicalMemoryPtr(hPhysMemHandle, dx /* segment addr */, bx /* offset */); fp.p = lpPhysPtr; /* use pointer lpPhysPtr */ } Glenn Boozer glenn@imagen.com QMS [PostScript is what we do best]