IO.DLL
SynopsisIO.DLL allows seamless port I/O operations
for Windows 95/98/NT/2000/XP using the same library.
Introduction
In the pre-Windows days, it was a relatively simple matter
to access I/O ports on a typical PC. Indeed, nearly every
language sported a special command for doing so. As Windows
emerged and gradually evolved, this flapping in the wind
behaviour could no longer be tolerated because of operating
system's ability to virtualize hardware.
Virtualizing hardware means that an application (typically
a DOS box in Windows) believes it is talking directly to a
physical device, but in reality it is talking to a driver that
emulates the hardware, passing data back and forth as
appropriate. This is how you are able to open dozens of DOS
boxes in your Windows session, each one with the strange
notion that it has exclusive access to peripherals such as the
video adapter, keyboard, sound card and printer.
If one were to rudely bang out data to an I/O port that
Windows thought it was in full control of, the "official bad
thing" could occur, the severity of which depending upon the
exact hardware that was being accessed. Actually, with the
virtualization just mentioned, it is quite improbable that
Windows would permit anything too nasty from occuring.
Windows 95/98 actually does allow I/O operations be
executed at the application level, although you'd be hard
pressed to find a language that supports this directly.
Typically the programmer will have to resort to assembly
language for this kind of low-level control. If you know what
you are doing, this can be a quick and easy way to access I/O
ports. Of course, not everyone knows, or desires to learn
80x86 assembly programming just because they want to turn on a
lamp from their computer. However, the unwillingness to learn
assembly language becomes rather trivial when faced with 9x's
big brother.
Windows NT/2000/XP, being the secure operating system that
it is, does not permit port I/O operations at the application
level at all. Period. A program with inline IN and OUT
assembly instructions that runs perfectly on Windows 95/98
will fail horribly when it comes to Windows NT/2000/XP.
Windows NT/2000/XP does, however, allow I/O instructions in
its kernel mode drivers. A kernel mode driver runs at the most
priviledged level of the processor and can do whatever it
pleases, including screwing up the system beyond repair, thus
writing a kernel mode driver is not for the feint of heart.
If you were to take it upon yourself to wade through the
documentation of the Windows NT/2000/XP ddk and piece together
a driver that was callable by your application to do the I/O
instructions on behalf of your application, you'd probably
notice something not too pleasant--this sort of access is
painfully slow. The call from application level to system
level typically takes about one millisecond. Compare this to
the one microsecond that a normal I/O access takes. To further
the insult, you are at the whim of the operating system. If it
has tasks which it believes are of higher priority than your
lowly call to your driver, it will perform them, making
precise timing nearly impossible.
Obviously, writing a driver that does acts a proxy for the
I/O calls isn't the most ideal solution. There is, however, a
solution for NT/2000/XP that allows the same convienience of
inline assembly language that 95/98 does.
As mentioned, a kernel mode driver can do whatever it
wants. The implication here is that if another kernel mode
driver shut off application access to the I/O ports, it should
be possible for another kernel mode driver to turn it back on.
This is where IO.DLL enters the picture.
LicensingIO.DLL is completely free! However, you may
not:
- Charge others for it in any way. For example, you cannot
sell it as a stand alone product.
- Charge for an IO.DLL wrapper, such as an OCX or Delphi
control whose purpose is just to put a fancy interface on
IO.DLL. I consider these to be "derived works" and they must
be provided free of charge.
- Claim that it is your property.
Also, the author
(that's me) cannot be held liable due to io.dll's failure to
perform. As with most free stuff, you are on your own.
Source Code and Special ModificationsThe source code
is available for $1,000 US.
I'm willing to work with people should they require a
special modification to IO.DLL. For example, you might have a
strict timing requirement of some sort that can only be done
in kernel mode. For a fee, I will modify IO.DLL and/or the
embedded kernel mode driver for the task at hand.
Description of IO.DLL
IO.DLL provides a useful set of commands for reading and
writing to the I/O ports. These commands are consistent
between 95/98 and NT/2000/XP. Furthermore, there is no need
for the programmer to learn assembly language or muck with
kernel mode drivers. Simply link to the DLL and call the
functions. It's that easy.
Windows NT/2000/XP is accomodated through the use of a
small kernel mode driver that releases the ports as needed to
the application. This driver is embedded in the DLL and is
installed if Windows NT/2000/XP is determined to be the
underlying operating system.
Due to the very minor overhead involved in dynamically
linking to IO.DLL, and the optimized functions contained
within, access to I/O ports is nearly as fast as if it was
written in raw assembler and inlined in your application. This
holds true for both Windows 95/98 and Windows NT/2000/XP.
Before moving on, it is probably prudent to mention that
the technique employed in IO.DLL for releasing the ports to
the application level isn't, strictly speaking, the proper way
to do things. The proper way is to have a virtual device
driver for Windows 95/98 and a kernel mode driver for Windows
NT/2000/XP. This isn't very practical for many people though,
nor is it really necessary. There are several successful
commercial products on the market that do exactly what IO.DLL
does. Let it be noted though that some of them are shady with
their explanation of how their product works, meanwhile
charging $500 or more for it.
What Ports can IO.DLL Access?IO.DLL is capable of
accessing only those ports that are inherent to the processor
itself through the IN and OUT instructions. This means that it
cannot access most add-in parallel ports or com ports because
they are typically memory-mapped these days rather than
IO-mapped. IO.DLL also cannot access virtual devices. Here is
a table of what ports can be accessed:
IO Address |
Device |
000-01F |
DMA controller #1 |
020-03F |
Interrupt controller |
040-05F |
Timer |
060-06F |
Keyboard controller |
070-07F |
Real-time clock, CMOS Memory, NMI mask |
080 |
manufacturer's diagnostics checkpoint |
080-09F |
DMA page register |
0A0-0BF |
Interrupt controller #2 |
0C0-0DF |
DMA controller #2 |
0F0-0FF |
Math Coprocessor |
170-177 |
Hard disk (secondary) |
1F0-17F |
Hard disk |
200-207 |
Game I/O |
278-27F |
LPT 2 |
2C0-2DF |
EGA #2 |
2E8-2EF |
COM 4 |
2F8-2FF |
COM 2 |
300-31F |
Prototype card |
370-377 |
FDC (secondary) |
378-37F |
LPT 1 |
380-38F |
SDLC |
3A0-3AF |
bisynchronous port #1 |
3B0-3BF |
MDA |
3C0-3CF |
EGA |
3D0-3DF |
CGA and EGA |
3E8-3EF |
COM 3 |
3F0-3F7 |
FDC |
3F8-3FF |
COM 1 |
Download
io.zip
46K (Contains all the files) io.dll
46K
The following two files are for C++ users. There is more
info on these in the prototypes section.
io.cpp
1.3K io.h 1.2K
C/C++ Prototypesvoid WINAPI PortOut(short int Port, char Data);
void WINAPI PortWordOut(short int Port, short int Data);
void WINAPI PortDWordOut(short int Port, int Data);
char WINAPI PortIn(short int Port);
short int WINAPI PortWordIn(short int Port);
int WINAPI PortDWordIn(short int Port);
void WINAPI SetPortBit(short int Port, char Bit);
void WINAPI ClrPortBit(short int Port, char Bit);
void WINAPI NotPortBit(short int Port, char Bit);
short int WINAPI GetPortBit(short int Port, char Bit);
short int WINAPI RightPortShift(short int Port, short int Val);
short int WINAPI LeftPortShift(short int Port, short int Val);
short int WINAPI IsDriverInstalled();
To use IO.DLL with Visual C++/ Borland C++, etc, you'll
need to use LoadLibrary and GetProcAddress. Yes, it's more of
a pain than using a .lib file, but because of name mangling,
it's the only reliable way of calling the functions in IO.DLL.
I've gone ahead and done the dirty work for you:
io.cpp io.h
Just save these two files and include them in your project.
For a Visual C++, you may need to add #include "StdAfx.h" at
the top of io.cpp otherwise the compiler will whine at you.
These two files take care of calling LoadLibrary and all
the neccessary calls to GetProcAddress, making your life happy
once again.
The only step you are required to do is call
LoadIODLL somewhere at the beginning of your program.
Make sure you do this or you will find yourself faced with all
sorts of interesting crashes.
Please let me know if you find any errors in the above two
files. They are new and haven't been tested all that much.
Delphi Prototypesprocedure PortOut(Port : Word; Data : Byte);
procedure PortWordOut(Port : Word; Data : Word);
procedure PortDWordOut(Port : Word; Data : DWord);
function PortIn(Port : Word) : Byte;
function PortWordIn(Port : Word) : Word;
function PortDWordIn(Port : Word) : DWord;
procedure SetPortBit(Port : Word; Bit : Byte);
procedure ClrPortBit(Port : Word; Bit : Byte);
procedure NotPortBit(Port : Word; Bit : Byte);
function GetPortBit(Port : Word; Bit : Byte) : WordBool;
function RightPortShift(Port : Word; Val : WordBool) : WordBool;
function LeftPortShift(Port : Word; Val : WordBool) : WordBool;
function IsDriverInstalled : Boolean;
Important! To use these functions in your Delphi
program, the correct calling convention of stdcall is
required. For example:
procedure PortOut(Port : Word; Data : Byte); stdcall; external 'io.dll';
Visual Basic PrototypesPrivate Declare Sub
PortOut Lib "IO.DLL" (ByVal Port As Integer, ByVal Data As
Byte) Private Declare Sub PortWordOut Lib "IO.DLL" (ByVal
Port As Integer, ByVal Data As Integer) Private Declare Sub
PortDWordOut Lib "IO.DLL" (ByVal Port As Integer, ByVal Data
As Long) Private Declare Function PortIn Lib "IO.DLL"
(ByVal Port As Integer) As Byte Private Declare Function
PortWordIn Lib "IO.DLL" (ByVal Port As Integer) As
Integer Private Declare Function PortDWordIn Lib "IO.DLL"
(ByVal Port As Integer) As Long Private Declare Sub
SetPortBit Lib "IO.DLL" (ByVal Port As Integer, ByVal Bit As
Byte) Private Declare Sub ClrPortBit Lib "IO.DLL" (ByVal
Port As Integer, ByVal Bit As Byte) Private Declare Sub
NotPortBit Lib "IO.DLL" (ByVal Port As Integer, ByVal Bit As
Byte) Private Declare Function GetPortBit Lib "IO.DLL"
(ByVal Port As Integer, ByVal Bit As Byte) As
Boolean Private Declare Function RightPortShift Lib
"IO.DLL" (ByVal Port As Integer, ByVal Val As Boolean) As
Boolean Private Declare Function LeftPortShift Lib "IO.DLL"
(ByVal Port As Integer, ByVal Val As Boolean) As
Boolean Private Declare Function IsDriverInstalled Lib
"IO.DLL" As Boolean
Function DescriptionsPlease refer to the prototype
for the particular language you are using.
PortOut Outputs a byte to the specified port.
PortWordOut Outputs a word (16-bits) to the
specified port.
PortDWordOut Outputs a double word (32-bits) to
the specified port.
PortIn Reads a byte from the specified port.
PortWordIn Reads a word (16-bits) from the
specified port.
PortDWordIn Reads a double word (32-bits) from
the specified port.
SetPortBit Sets the bit of the specified port.
ClrPortBit Clears the bit of the specified port.
NotPortBit Nots (inverts) the bit of the
specified port.
GetPortBit Returns the state of the specified
bit.
RightPortShift Shifts the specified port to the
right. The LSB is returned, and the value passed becomes the
MSB.
LeftPortShift Shifts the specified port to the
left. The MSB is returned, and the value passed becomes the
LSB.
IsDriverInstalled Returns non-zero if io.dll is
installed and functioning. The primary purpose of this
function is to ensure that the kernel mode driver for
NT/2000/XP has been installed and is accessible.
Other InformationAn excellent document about the
standard parallel port can be found here.
|