Back
Last edit: 99-05-05
Graham Wideman |
Delphi
|
Hardware I/O Port Programming with Delphi
and NT
Article created: 98-06-01
Minor edits: 98-06-18 |
Orientation
Engineers and others interested in using their PCs to manipulate
external
hardware have long been drawn to TurboPascal/Delphi, because of the
combination
of power, predictability and speedy compilation that it offers while
avoiding
some of the complexity of C++. You can afford to be a specialist
at something other than the programming language. This was great
under DOS, and even under W3.1 and W95 with Delphi 1. But with
Delphi
2 and 3, and Win NT, the direct hardware manipulation features are not
so directly accessible.
This article covers these general topics:
-
This Page
- The port I/O instructions themselves
-
Getting NT to give you permission to use them. A driver that does
the job.
-
Having your program install the driver transparently with minimum fuss.
-
A Delphi unit to handle it all.
-
Alternate Approaches
-
Resources and References
-
Copyright status
-
Other Pages
- Documentation for
gwiopm.sys driver and gwiopm.pas interface unit for Delphi
-
Documentation for PortTest
test program
-
If you are reading this page off my web site, then download all source
code, executables and these pages here: gwiopm.zip
(if reading from CD or hard-drive, you already got this zip and
expanded
it, so the link is disabled.)
Version applicability: This article applies to (and the software has
been
tested with) Windows NT 4 (SP3). It uses techniques that have applied
certainly
since 1996 (hence should work with NT 3.5x). It has not been
tried
with NT 5.
The Port I/O Instructions
This is actually the easy part. In Turbo/Borland Pascal and
Delphi
1 there are Port arrays which you can read and write (like any other
array).
These are absent from Delphi 2 and 3, presumably because of the
additional
wrinkles added by Win32 and NT programming. No matter, we can
easily
add PortIn and PortOut functions/procedures to call the CPU's I/O
instructions.
These are in unit gwportio.pas, and are similar to other such
functions
floating around the web. (Onno Kortmann authored a unit
called
simport.pas with a slightly cooler object-based implementation
that
looks like the old Port arrays. I decided to go for simplest
implementation,
so mine are just functions and procedures.)
Getting Permission From NT
Under NT, "user-mode" code (ie: applications written by mere mortals)
is
not allowed to access hardware directly, hence when your application
attempts
to execute an I/O instruction you get an exception. The idea is,
of course, that hardware resources are things that no application
should
just take over at will, instead it should be up to the operating system
(and its drivers) to arbitrate between different apps requests to use
those
resources.
That's the theory. Turns out that the NT kernel maintains a
map
of I/O port addresses that each process is allowed to access, and for
your
apps that's normally set to "none". But we can tell NT to use a
different
I/O Permissions Map (IOPM) for our process and thereby gain access to
the
ports. This approach is of course very naughty from a disciplined
OS standpoint, so not recommended for widely distributed commercial
apps.
But for those times when you just need to hack on some hardware, who
has
time to write a proper NT device driver?
The only problem is that user-mode code is not allowed to execute
the
kernel functions to change the permissions map. The workaround for that
problem is to create an NT driver (drivers have sufficient privileges)
to twiddle the IOPM at the request of your app. Just such a driver, giveio.sys,
has been floating around the net since '96, authored by Dale Roberts in
conjunction with a May 96 Dr Dobbs article.
Improved Driver and Delphi Interface Unit
Giveio.sys demonstrates the concept and most importantly clues us in to
the undocumented kernel functions, but it throws open access to all I/O
ports. So I decided to take a crack at an improved driver that
would
allow giving access only to selected ports, and would provide a few
diagnostic
functions in to the bargain. The result is gwiopm.sys (source
code
included if you want to browse). A Delphi interface to
gwiopm.sys
is provided in gwiopm.pas.
If you too want to have a go at device driver development, be
forewarned
that the only known way to compile one is with the MS NT Device Driver
Development Kit (NT DDK), which in turn needs the Win32 SDK, and relies
on some pretty specific feature of the MS C environment (Visual
Studio).
(Oh, and supposedly you can't compile on a W95 machine
either...)
This leads to the somewhat incongruous necessity to install about about
500 Meg of cra... er, I mean, "supporting code" in order to compile a
couple
of hundred lines of source code into a 4K executable.
Recommended:
Art Baker's "The Windows NT Device Driver Book" (Prentice Hall '97)
helps
to smooth out the bumps.
Installing a Driver Without Hassle for Users
So now we have a driver that can give our application permission to use
I/O instructions. To install this driver you could create an
installation
program, or you could manually edit the registry to have NT load up the
driver on boot up. But though you're not really going to
distribute
your hardware hack, there are just a
couple of other people who'll
need to install it, right? And making them hassle with installing a
driver
is tedious.
For this reason, gwiopm.pas includes functions that your
Delphi
app can call to transparently install the gwiopm driver on the fly,
start
it up and then later stop and uninstall it after you are done. (I first
learned about the functions to do this from example loaddrv.c code
written
by Paula Tomlinson, associated with a Windows Dev Journal article.)
Alternative Approaches
I/O Port Driver: The port I/O approach shown here takes
the
point of view that if you're wanting to manipulate ports directly, you
probably want as few extraneous mechanisms involved as possible. And if
you have dedicated hardware attached to your PC, in many instances you
probably have a single piece of dedicated software to control it.
So there's no need to worry about arbitration. For these reasons, the
approach
described in this article provides actual cpu I/O instructions right in
your Delphi/assembler code.
However, in other situations you may genuinely need a more civilized
approach: even though you are manipulating the hardware in a custom
manner,
you might still like your port usage to be arbitrated by a "good
citizen"
driver. An example of this scenario would be where you are
twiddling
some peripheral that's attached to a PC parallel printer port...
but you don't want to clobber NT's use of that same port.
This, of course, requires a more sophisticated driver that request
and
gains control of the port in the polite and proper manner, and
intermediates
your app's "requests" for I/O, translating them into actual I/O
actions.
Such an approach is taken by Victor I. Ishikeev in his TVicPort driver
and accompanying examples for Win95 and NT. Find it at the main Delphi
freeware/shareware sites (look for recent versions as early ones didn't
cover NT.) Or one could look at the samples in the NT
DDK if you are considering writing your own.
Writing Drivers in Delphi: On the surface, it would
appear
that you can't use Delphi to write drivers. (The advance word on D4
indicates
some features for writing services... it remains to be seen whether
this
includes device drivers, and in any case this is a feature of the
expensive
version.) The primary reason is that Delphi lacks the
compile/link
functions to produce a .sys file, not to mention missing the
Delphi-ized
DDK header files.
However, you can get generic "adapter" drivers which will
callback
your Delphi code. There is some overhead involved, but for some
situations
it could be just the ticket. Other related products offer
"Wizard"-style
driver development, which again avoids the necessity to understand
device
drivers in depth (one hopes) See:
Resources
Start at Cherry Hill Software
Copyright Status
Please feel free to distribute or publish this article and
associated
code, provided some note of credit for me remains attached. Permission
is also granted to include this article on CDROM collections, again
provided
credit remains attached. Thanks!
Back
Go to: