📄 jan axelson's parallel port faq.htm
字号:
<P>1. Write a couple of values to each of the addresses and read back the
result. If the values match, the port exists. This won't detect a port
where access is denied by a system-level driver, or a port in PS/2-input
mode (though you can also test for this). Requires an Inpout DLL or other
driver for port I/O. <BR><BR>2. 16-bit only: Use Vbasm's Peek function to
read the addresses stored in the BIOS data area at 40:08 through 40:0D.
May not include all ports.<BR><BR>3. Under Windows 95, the port addresses
are stored in the registry, under
<BR><BR>[HKEY_LOCAL_MACHINE\Enum\Root\<BR>[HKEY_LOCAL_MACHINE\Enum\BIOS\<BR>[HKEY_DYN_DATA\Config
Manager\Enum\ <BR><BR>You can read the registry data with API calls.</P>
<P>From Dominic On: Option 3 is difficult in Windows 95. I tested many PCs
and found that the byte location of the port address is different from one
PC to another. But in windows NT it's OK. Based on the 3 options above, I
came up with a solution: 1. Use an API call to detect which platform is
running. (95 or NT) 2. If 95, use Toolhelp32ReadProcessMemory() function
to find the port address. 3. If NT, read the port address from the
registry. My application/DLL runs OK in windows 95. But when it runs in
NT, I receive an error that Toolhelp32ReadProcessMemory() is not found in
the Kernel32. Therefore, when I release my program/DLL, I have to compile
conditionally in 2 versions (Windows 95 and NT).</P>
<P>And Bill McCarthy contributes this code that shows how to use
Toolhelp32ReadProcessMemory to obtain port addresses:</P>
<P><FONT face="Courier New, Courier, mono">Declare Function
Toolhelp32ReadProcessMemory Lib "KERNEL32" _ <BR> (ByVal
th32ProcessID As Long, _ <BR> ByVal lpBaseAddress As Long, _
<BR> lpBuffer As Any, _ <BR> ByVal cbRead As Long, _
<BR> ByRef lpNumberOfBytesRead As Long) <BR>As Long </FONT></P>
<P><FONT face="Courier New, Courier, mono">Sub Main() <BR>Dim i As Long,
rtn As Long <BR>Dim portAddresses(3) As Integer <BR>Dim cbLen As Long
<BR></FONT></P>
<P><FONT face="Courier New, Courier, mono">cbLen = 8 <BR>rtn =
Toolhelp32ReadProcessMemory _<BR> </FONT><FONT
face="Courier New, Courier, mono">(0&, _<BR>
&H408&, _<BR> </FONT><FONT
face="Courier New, Courier, mono">portAddresses(0), _<BR> 8,
_<BR> cbLen) <BR>'Debug.Print rtn, cbLen <BR></FONT></P>
<P><FONT face="Courier New, Courier, mono">For i = 0 To 3
<BR> If portAddresses(i) = 0 Then Exit For
<BR> Debug.Print _<BR></FONT><FONT
face="Courier New, Courier, mono"> "port address " & i
& " = &H" & Hex$(portAddresses(i)) <BR>Next i <BR></FONT></P>
<P><FONT face="Courier New, Courier, mono">End Sub</FONT></P>
<P>Peter Burke offers this code that dynamically loads the
Toolhelp32ReadProcessMemory <BR>function under WIN9X only, thus allowing a
single binary for both WIN9X/NT.<BR><BR><FONT
face="Courier New, Courier, mono">#include <windows.h><BR>#define
TEST_WINDOWS_NT (!(GetVersion() &
0x80000000))<BR>/*********************************************************************<BR>******************/<BR>int
GetParallelControllerKey(char *parKey)<BR>{<BR>HKEY hKey;<BR>char
myData[255];<BR>LONG res;<BR>DWORD mySize;<BR>FILETIME
ftLastWriteTime;<BR><BR>if
(NULL==parKey)<BR>return(-1);<BR><BR>*parKey=0;<BR><BR>char
myKey[255];<BR>sprintf(myKey,"HARDWARE\\DESCRIPTION\\System");<BR><BR>res
= RegOpenKeyEx(HKEY_LOCAL_MACHINE,myKey, 0, KEY_READ,
&hKey);<BR><BR>if (res!=ERROR_SUCCESS)<BR>return(-1);<BR><BR>DWORD
dwIndex1;<BR>char myKey1[255];<BR>for
(dwIndex1=0;dwIndex1<=10;dwIndex1++)<BR>{<BR>mySize=sizeof(myData);<BR>res
=
<BR>RegEnumKeyEx(hKey,dwIndex1,myData,&mySize,NULL,NULL,NULL,&ftLastWriteT<BR>ime);<BR><BR>if
(res==ERROR_SUCCESS) // ERROR_SUCCESS
1<BR>{<BR>strcpy(myKey1,myKey);<BR>strcat(myKey1,"\\");<BR>strcat(myKey1,myData);<BR><BR>HKEY
hKey1;<BR>res = RegOpenKeyEx(HKEY_LOCAL_MACHINE,myKey1, 0, KEY_READ,
<BR>&hKey1);<BR><BR>if
(res!=ERROR_SUCCESS)<BR>return(-1);<BR><BR>DWORD dwIndex2;<BR>char
myKey2[255];<BR>for
(dwIndex2=0;dwIndex2<=10;dwIndex2++)<BR>{<BR>mySize=sizeof(myData);<BR>res
=
<BR>RegEnumKeyEx(hKey1,dwIndex2,myData,&mySize,NULL,NULL,NULL,&ftLastWrite<BR>Time);<BR><BR>if
(res==ERROR_SUCCESS) // ERROR_SUCCESS
2<BR>{<BR>strcpy(myKey2,myKey1);<BR>strcat(myKey2,"\\");<BR>strcat(myKey2,myData);<BR><BR>HKEY
hKey2;<BR>res = <BR>RegOpenKeyEx(HKEY_LOCAL_MACHINE,myKey2, 0, KEY_READ,
&hKey2);<BR><BR>if (res!=ERROR_SUCCESS)<BR>return(-1);<BR><BR>DWORD
dwIndex3;<BR>for
(dwIndex3=0;dwIndex3<=10;dwIndex3++)<BR>{<BR>mySize=sizeof(myData);<BR>res
=
<BR>RegEnumKeyEx(hKey2,dwIndex3,myData,&mySize,NULL,NULL,NULL,&ftLastWrite<BR>Time);<BR><BR>if
(res==ERROR_SUCCESS) // <BR>ERROR_SUCCESS 3<BR>{<BR>if
<BR>(0==strcmp(myData,"ParallelController")
)<BR>{<BR><BR>strcpy(parKey,myKey2);<BR><BR>strcat(parKey,"\\");<BR><BR>strcat(parKey,myData);<BR>return(0);<BR>}<BR>}
// ERROR_SUCCESS 3<BR><BR>} // for (dwIndex3<BR><BR>} // // ERROR_SUCCESS
2<BR><BR>} // for (dwIndex2<BR><BR>} // ERROR_SUCCESS 1<BR><BR>} // for
(dwIndex1<BR><BR>return(-1);<BR>} //GetParallelControllerKey
<BR><BR>/*********************************************************************<BR>******************/<BR>int
GetAddressLptPortInTheRegistry(int myPort)<BR>{<BR>HKEY phkResult;<BR>char
myKey[255];<BR>char myData[255];<BR>LONG res;<BR>DWORD mySize;<BR>DWORD
myType;<BR><BR>res=GetParallelControllerKey(myKey);<BR>if (res <
0)<BR>return(-1);<BR><BR>sprintf(myData,"%s\\%d",myKey,myPort-1);<BR><BR>res
= RegOpenKeyEx(HKEY_LOCAL_MACHINE,myData, 0, KEY_READ,
<BR>&phkResult);<BR>if (res !=
ERROR_SUCCESS)<BR>return(-1);<BR><BR>mySize=sizeof(myData);<BR>myType=REG_BINARY;<BR><BR>res=RegQueryValueEx(<BR>phkResult,
// handle to key to query<BR>"Configuration Data", // address of name of
value to query<BR>NULL, // reserved<BR>&myType, // address of buffer
for value type<BR>(unsigned char *)myData, // address of data
buffer<BR>&mySize // address of data buffer size<BR>);<BR>if (res !=
ERROR_SUCCESS)<BR>return(-1);<BR><BR>//printf("config data port %d 0x14 =
0x%02X 0x15 =
<BR>0x%02X\n",myPort,myData[0x14],myData[0x15]);<BR>return(myData[0x14] |
myData[0x15]<<8 );<BR>}<BR><BR>typedef BOOL (CALLBACK *
PROCTYPE_Toolhelp32ReadProcessMemory)( DWORD <BR>, LPCVOID, LPVOID, DWORD
,LPDWORD);<BR>/*********************************************************************<BR>******************/<BR>int
GetAddressLptPortInTheMemory(int myPort)<BR>{<BR>HINSTANCE hDLL=NULL; //
Handle to DLL<BR>PROCTYPE_Toolhelp32ReadProcessMemory
myProcPointer=NULL;<BR><BR>hDLL = LoadLibrary("kernel32");<BR>if
(hDLL==NULL)<BR>return(-1);<BR><BR>myProcPointer=(PROCTYPE_Toolhelp32ReadProcessMemory)GetProcAddress(hDL<BR>L,"Toolhelp32ReadProcessMemory");<BR>if
(myProcPointer==NULL) /*handle the error*/<BR>{ <BR>FreeLibrary(hDLL);
<BR>return -1;<BR>}<BR><BR>int portAddresses[]={0,0,0,0,0};<BR>BOOL
rtn=0;<BR>DWORD cbLen=0;<BR>//rtn = Toolhelp32ReadProcessMemory <BR>rtn =
myProcPointer<BR>(0,<BR>(LPCVOID *)
0x408,<BR>portAddresses,<BR>8,<BR>NULL) ;<BR><BR>FreeLibrary(hDLL);
<BR><BR>if (rtn==0)<BR>return(-1);<BR><BR>if
(portAddresses[myPort-1]<=0)<BR>return(-1);<BR><BR>if
(portAddresses[myPort-1]>=0x1000)<BR>return(-1);<BR><BR>return(portAddresses[myPort-1]);<BR>}<BR><BR>/*********************************************************************<BR>******************/<BR>int
GetAddressLptPort(int
myPort)<BR>{<BR>if(myPort<1)<BR>return(-1);<BR><BR>if(myPort>3)<BR>return(-1);<BR><BR>if
(TEST_WINDOWS_NT)<BR>return(GetAddressLptPortInTheRegistry(myPort));<BR><BR>return(GetAddressLptPortInTheMemory(myPort));<BR>}<BR></FONT></P>
<P><I>Q: How can I access ports using Perl?</I></P>
<P>A: (from Ron Glick)</P>
<P>You must have the Win32::API module (available at www.cpan.org)
installed.</P>
<P><FONT face="Courier New, Courier, mono">use Win32::API; #load API
module to interface DLL's <BR>$GetPortVal= new Win32::API("inpout32",
"Inp32", [I], I); #import Inp32 from DLL <BR>$SetPortVal= new
Win32::API("inpout32", "Out32", [I,I], I); #import Out32 from
DLL<BR>$input= $GetPortVal->Call(0x378) & 255; #get and display
current value of address <BR>378 hex print "$input\n";
<BR>$return=$SetPortVal->Call(0x378,35); #set pins 2,3,7 <BR>$input=
$GetPortVal->Call(0x378) & 255; #get and display updated value of
address 378 hex <BR>print "$input\n";</FONT></P>
<H2>What Type of Port? </H2>
<P><I>Q: How can I tell what type of port I have? </I></P>
<P>A: These are some ways to detect what type of port a system has: SPP
(original), PS/2 (simple bidirectional), EPP, or ECP. The explanations
assume that you have some familiarity with the different port types and
how to access port registers. These tests detect only what type of port is
currently enabled! If the advanced modes are disabled on the port
controller chip, the tests won't detect them. </P>
<P>On many ports that support advanced modes, you can configure the port
either in the CMOS setup, or with jumpers, or with configuration software
that comes with the port. Most have an option that causes the port to
emulate the original SPP, plus one or more options that enable the
advanced modes. If the port is configured as an SPP, the advanced modes
will be locked out and the port will fail any tests for PS/2, EPP, or ECP
abilities. </P>
<P>Detecting an ECP </P>
<P>In testing a port, you might think that the first step would be to test
for an SPP, and work your way on up from there. But if the port is an ECP,
and it happens to be in its internal SPP-emulation mode, the port will
fail the PS/2 (bidirectional) test. For this reason, I begin by testing
for an ECP, and work down from there. This is the method Microsoft's ECP
document (in the Developer's Network CD-ROM) recommends for detecting an
ECP: 1. Read the ECP's extended control register (ECR) at base address +
402h and verify that bit 0 (fifo empty) =1 and bit 1 (fifo full) =0. These
bits should be distinct from bits 0 and 1 in the port's control register
(at base address + 2). You can verify this by toggling one of the bits in
the control register, and seeing that the corresponding bit in the ECR
doesn't change. 2. A further test is to write 34h to the ECR and read it
back. Bits 0 and 1 in the ECR are read-only, so if you read 35h, you
almost certainly have an ECP. If an ECP exists, you can read and set the
ECP's internal mode in the ECR. (See below.) </P>
<P>Detecting an EPP </P>
<P>In addition to the SPP's three registers, an EPP has four additional
registers, at base address + 3 through base address + 6. These additional
registers provide a way to test for the presence of an EPP, by writing a
couple of values to one of the EPP registers and reading them back, much
like you would test for an SPP. If the reads are successful, the register
exists and you probably have an EPP. I'm not sure if this test works on
all EPPs. Because the EPP handshake doesn't complete, there's no guarantee
of the contents of the register after the transfer times out. But on the
tests I've done, I was able to read back the values written. Be sure to
clear the EPP timeout bit (bit 0 of the status port, at base address + 1)
after each read or write. Unfortunately, the method for clearing the bit
varies with the controller chip. On some ports, you clear the bit by
writing 1 to it. On others, simply reading the status register clears the
bit. And, though I haven't seen any controllers that clear the bit in the
conventional way, by writing 0 to it, you may as well do that too, just to
be safe. </P>
<P>Beware #1: on SMC's chips (& maybe others), a set timeout bit can
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -