You are on page 1of 18

Writing DLLs

and calling DLLs from


LabView

© by
Heinz Rongen
Forschungszentrum Jülich
Zentrallabor für Elektronik
52425 Jülich, Germany

email: H.Rongen@fz-juelich.de

Writing DLLs and LabView: (1)

Heinz Rongen Forschungszentrum Jülich, ZEL 52425 Jülich, Germany


Tel: +49-(0)2461-614512 Fax: +49-(0)2461-613990 Email: H.Rongen@fz-juelich.de
1. What are DLLs ? _________________________________________________________ 3
1. The Anatomy of a DLL ____________________________________________________ 3
1.1. Outline of the Source Code of a DLL ________________________________________ 4
2. Example – Writing a DLL with Borland C/C++ (5.02)____________________________ 5
2.1. C Language Source File __________________________________________________ 5
2.2. The Header File ________________________________________________________ 7
2.3. The Module Definition File ________________________________________________ 7
2.4. Building the Project _____________________________________________________ 7
3. Calling the DLL from LabView ______________________________________________ 8
3.1. Advanced Topic – Array and String Options __________________________________ 9
3.1.1. Arrays of Numeric Data__________________________________________________________ 9
3.1.2. String Data ___________________________________________________________________ 9
3.2. Array and String Tips __________________________________________________ 10
3.3. Troubleshooting the Call Library Function and Your DLL ______________________ 11
3.4. Important Reminders and Quick Reference __________________________________ 12
3.5. Datatypes ____________________________________________________________ 13
4. Hardware access: ________________________________________________________ 14
4.1. Hardware access on different Windows Platforms: ____________________________ 14

Writing DLLs and LabView: (2)

Heinz Rongen Forschungszentrum Jülich, ZEL 52425 Jülich, Germany


Tel: +49-(0)2461-614512 Fax: +49-(0)2461-613990 Email: H.Rongen@fz-juelich.de
1. What are DLLs ?

A Dynamic Link Library (DLL) is a file of code,


containing functions,
that can be called from other executable code.
(either an application or another DLL)

Programmers use DLLs to provide code that they can reuse and to
parcel out distinct jobs.
Unlike an executable (EXE) file, a DLL cannot be directly run.
DLLs must be called from other code that is already executing.

In more understandable words, a DLL is a file which does a particular job, and allows
other programs to use its efforts in assisting the program's job. Some programs use a
DLL so that they won't need to spend time figuring out how to do that job. For
example, Microsoft has a DLL comctl32.dll which does all the user interface jobs
(toolbars, text boxes, scroll bars, etc). So, other programs use that DLL so they won't
have to create their own edit boxes, etc.

When a program requires a DLL to run, and can't find it, it won't be able to run
because its suddenly missing the DLL to perform some of its critical work. We've all
used DLLs before and we're using them now. They're required to run all Windows
programs, including Windows but you never actually see them at work. There are
different versions of the same file name. Just because the file appears to be the
same doesn't mean it is. To check what version the file is, open Windows Explorer,
locate the file and right click on it. Select Properties and click on the Version tab. If
there is no version tab then the file does not have a version number. Generally, if you
have a newer version of a file, don't replace it with an older version. Due to popular
request, we are slowly adding version numbers to each individual file. This will take
quite a while. Please bear with us.

1. The Anatomy of a DLL


Dynamic linking is a mechanism that links applications to libraries at run time. The
libraries remain in their own files and are not copied into the executable files of the
applications. DLLs link to an application when the application is run, rather than when
it is created. DLLs may contain links to other DLLs.

Note: Many times, DLLs are placed in files with different extensions such as .EXE,
.DRV or .DLL.

Applications and DLLs can link to other DLLs automatically if the DLL linkage is
specified in the IMPORTS section of the module definition file as part of the compile
or you can explicitly load them using the Windows LoadLibrary function.

Writing DLLs and LabView: (3)

Heinz Rongen Forschungszentrum Jülich, ZEL 52425 Jülich, Germany


Tel: +49-(0)2461-614512 Fax: +49-(0)2461-613990 Email: H.Rongen@fz-juelich.de
1.1. Outline of the Source Code of a DLL

The following example code illustrates the basic structure of a DLL:

#include <windows.h>

BOOL WINAPI DllEntryPoint ( HINSTANCE hInstDLL,


DWORD fdwReason,
LPVOID lpvReserved )
{
if (!hDLL) hDLL = hInstDLL; // save DLL handle

switch (fdwReason)
{
case DLL_PROCESS_ATTACH: break;

case DLL_THREAD_ATTACH: break;

case DLL_PROCESS_DETACH: break;

case DLL_THREAD_DETACH: break;

}
return (TRUE);
}

As shown in the example code, in Win32 the DllEntryPoint function is the


main DLL entry point.

Note: the Dll-Entry-Point function name is different for different Compiler systems:
in Borland C/C++: DllEntryPoint
in Microsoft Visual C/C++: DllMain

The DllEntryPoint function is called when a DLL is loaded or unloaded. The


DllEntryPoint function is also called when a new thread is being created in a
process already attached to the DLL, or when a thread has exited cleanly.

Writing DLLs and LabView: (4)

Heinz Rongen Forschungszentrum Jülich, ZEL 52425 Jülich, Germany


Tel: +49-(0)2461-614512 Fax: +49-(0)2461-613990 Email: H.Rongen@fz-juelich.de
Finally, a DLL also contains functions that perform the activities the DLL expects to
accomplish. These functions are declared using the WINAPI keyword when
prototyping and declaring the functions.
Additionaly the EXPORTS section in module definition files must be used to export
these functions.

DWORD WINAPI Func1 (DWORD a, DWORD b)


{
DWORD c;
c = a + b;
return (c);
}

2. Example – Writing a DLL with Borland C/C++ (5.02)


In some cases, you may want to write your own DLL, for example, to communicate
with your own custom-built hardware. In this section, you will find sample code that
illustrates how to create a simple DLL.

To create a DLL, you will need the following four files:


♦ a C Language source file (required)
♦ a header file (optional – may be part of the source code)
♦ a module definition file
♦ a project file (or set compiler options to generate a DLL

2.1. C Language Source File


The code on the following page is the C language source file for the DLL we will
create in this example. It uses the standard WINAPI calling convention.

The example DLL defines three simple functions:


• add_num adds two integers
• avg_num finds the simple average of an array of numeric data
• numIntegers counts the number of integers in a string

In this example, we will create a VI that calls only one of these functions. As a further
exercise on your own, create VIs that use the Call Library Function to use the other
functions in the DLL.

After the listing of the source code for the DLL, you will also find listing for the custom
header file, ourdll.h.

Writing DLLs and LabView: (5)

Heinz Rongen Forschungszentrum Jülich, ZEL 52425 Jülich, Germany


Tel: +49-(0)2461-614512 Fax: +49-(0)2461-613990 Email: H.Rongen@fz-juelich.de
/* ourdll.c source code */
#include <windows.h>
#include "ourdll.h"

DWORD WINAPI Func1 (DWORD a, DWORD b)


{
DWORD c;
c = a + b;
return (c);
}

long WINAPI avg_num (float *a, long size, float *avg)


{
int i;
float sum=0;
if(a != NULL)
{
for(i=0;i < size; i++)
sum = sum + a[i];
}
else return (1);
*avg = sum / size;
return (0);
}

BOOL WINAPI DllEntryPoint ( HINSTANCE hInstDLL,


DWORD fdwReason,
LPVOID lpvReserved )
{
if (!hDLL) hDLL = hInstDLL; // save DLL handle

switch (fdwReason)
{
case DLL_PROCESS_ATTACH: break;

case DLL_THREAD_ATTACH: break;

case DLL_PROCESS_DETACH: break;

case DLL_THREAD_DETACH: break;

}
return (TRUE);
}

Writing DLLs and LabView: (6)

Heinz Rongen Forschungszentrum Jülich, ZEL 52425 Jülich, Germany


Tel: +49-(0)2461-614512 Fax: +49-(0)2461-613990 Email: H.Rongen@fz-juelich.de
2.2. The Header File
The following code lists the header file ourdll.h. All of the functions we have created
in the source code are available to other applications because they were exported
using the _declspec (dllexport) keyword.

DWORD WINAPI Func1 (DWORD a, DWORD b);


long WINAPI avg_num (float *a, long size, float *avg);

in Microsoft C++:
_declspec (dllexport) DWORD Func1 (DWORD a, DWORD b);
_declspec (dllexport) long avg_num (float *a, long size, float *avg);

2.3. The Module Definition File


A DLL can have a module definition file (.def) associated with it. The .def file contains
the statements for defining a DLL. e.g name of the DLL and the functions it exports.
The only mandatory entries in the .def files are the LIBRARY statement and the
EXPORT statement. The LIBRARY statement must be the first statement in the file.
The name specified in the LIBRARY statement identifies the library in the import
library of the DLL. The EXPORTS statement lists the names of the functions exported
by the DLL. If you were using the _stdcall calling convention in your DLL, then you
would have to use the module definition file to export the functions that the DLL
exposes, as shown below. This has to be done because the compiler mangles the
function names. Following is the listing of the module definition file.

LIBRARY ourDLL
EXPORTS Func1
avg_num

2.4. Building the Project


First, you will need to create the project. The steps to create a DLL are being
described in the C Compiler manual.
First create a new project by selecting New->Project from the File menu. Choose the
type as Dynamic Link Library. Name the project ourdll. Then create the header and C
source code files. Name them ourdll.h and ourdll.c. It is best to store all of these files
in the same directory. After adding these files to the project, compile the code into a
DLL.

Writing DLLs and LabView: (7)

Heinz Rongen Forschungszentrum Jülich, ZEL 52425 Jülich, Germany


Tel: +49-(0)2461-614512 Fax: +49-(0)2461-613990 Email: H.Rongen@fz-juelich.de
3. Calling the DLL from LabView
This simple DLL can now be called by using the LabVIEW Call Library Function. We
will create a VI called ARRAYAVG.VI that calls the avg_num function in ourdll.dll.
Place a Call Library Function icon on the block diagram and configure it to call the
avg_num function.

As specified in the previous diagram, first specify the location of the DLL by typing in
the path name. Also specify the name of the function you want to call in the DLL. In
this case it is avg_num. The calling convention for this function in the DLL is C.
The return type is Signed 32-bit Integer. The parameters to the function are an Array
Data Pointer to 4-byte Single precision floating point numbers, a 32-bit Signed
Integer which contains the size of the array, and a pointer to a 4-byte Single precision
floating point value, which will return the average of the elements in the array. Create
the front panel shown in the Figure.
Then, connect the appropriate controls and indicators to the Call Library Function
icon.

Writing DLLs and LabView: (8)

Heinz Rongen Forschungszentrum Jülich, ZEL 52425 Jülich, Germany


Tel: +49-(0)2461-614512 Fax: +49-(0)2461-613990 Email: H.Rongen@fz-juelich.de
After making the appropriate connections, run the VI.
Congratulations! You’ve written, compiled, and linked to your own DLL and called a
function within it from LabVIEW. If your DLL returns incorrect results or crashes,
verify the datatypes and wiring to see if you miswired the wrong type of information.

3.1. Advanced Topic – Array and String Options


This section of the document reviews some important concepts you should be
familiar with when using the Call Library Function to work with array and string
data. Operations on arrays and strings of data make use of pointers, so this
information will be useful in helping you implement the Call Library Functio n.

3.1.1. Arrays of Numeric Data


Arrays of numeric data can be of any integer type, or single (4-byte) or double (8-
byte) precision floating point numbers. When you pass an array of data to a DLL
function, you will see that you have the option to pass the data as an Array Data
Pointer or a LabVIEW Array Handle. When you pass an Array Data Pointer, you can
also set the number of dimensions in the array, but you do not include information
about the size of the array dimension(s). DLL functions either assume the data is of a
specific size or expect the size to be passed as a separate input. Also, because the
array pointer refers to LabVIEW data, never resize the array within the DLL. Doing
this may cause your computer to crash. If you must return an array of data, allocate
an array of sufficient size in LabVIEW, pass it to your function, and have it act as the
buffer. If the data takes less space, you can return the correct size as a separate
parameter and then on the calling diagram use array subset to extract the valid data.
Alternatively, if you pass the array data as a LabVIEW Array Handle then you can
use the LabVIEW CIN functions to resize the array within the DLL. In order to call
LabVIEW CIN functions from your DLL while using the C compiler you will have to
include the library, labview.lib found in LabVIEW/cintools/Win32 directory.

3.1.2. String Data


LabVIEW stores strings in a format similar to the Pascal string. The Call Library
Function works with LabVIEW String Handles or with either C or Pascal-style string
pointers. The difference between these formats is explained in the following
paragraphs. You can think of a string as an array of characters; assembling the
characters in order forms a string. LabVIEW stores a string in a special format in
which the first four bytes of the array of characters form a signed 32-bit integer that
stores how many characters appear in the string. Thus, a string with n characters will
require n + 4 bytes to store in memory. For example, the string text contains four
characters. When LabVIEW stores the string, the first four bytes contain the value 4
as a signed 32-bit number, and each of the following four bytes contains a character
of the string. The advantage of this type of string storage is that NULL characters are
allowed in the string. Strings are virtually unlimited in length (up to 2^31 characters).

The Pascal string format is nearly identical to the LabVIEW string format, but instead
of storing the length of the string as a signed 32-bit integer, it is stored as an
unsigned 8-bit integer. This limits the length of a Pascal style string to 255

Writing DLLs and LabView: (9)

Heinz Rongen Forschungszentrum Jülich, ZEL 52425 Jülich, Germany


Tel: +49-(0)2461-614512 Fax: +49-(0)2461-613990 Email: H.Rongen@fz-juelich.de
characters. A Pascal string that is n characters long will require n + 1 bytes of
memory to store.
The Pascal String Format C strings are probably the type of strings you will deal with
most commonly. The similarities between the C-style string and normal numeric
arrays in C becomes much more clear when one observes that C strings are
declared as char *, where char is typically an unsigned byte. C strings do not contain
any information that directly gives the length of the string, as do the LabVIEW and
Pascal strings. Instead, C strings use a special character, called the NULL character,
to indicate the end of the string. NULL is defined to have a value of zero in the ASCII
character set. Note that this is the number zero and not the character “0”. Thus, in C,
a string containing n characters requires n + 1 bytes of memory to store: n bytes for
the characters in the string, and one additional byte for the NULL termination
character. The advantage of C-style strings is that they are limited in size only by
available memory. However, if you are acquiring data from an instrument that returns
numeric data as a binary string, as is common with serial or GPIB instruments,
values of zero in the string are possible. For binary data where NULLs may be
present, you should probably use an array of unsigned 8-bit integers. If you treat the
string as a C-style string, your program will incorrectly assume that the end of the
string has been reached, when in fact your instrument is returning a numeric value of
zero.
When you pass string data to a DLL, you must follow the same guidelines as for
arrays. Specifically, never resize a string, concatenate a string, or perform operations
that may increase the length of string data passed from LabVIEW if you are using the
C or Pascal string pointers. If you must return data as a string, you should first
allocate a string of the appropriate length in LabVIEW, and pass this string into the
DLL to act as a buffer.

Note: To use the LabVIEW CIN function calls you must add labview.lib to your
project if you are using the Visual C++ compiler, or add labview.sym.lib to your
project if you are using the Symantec compiler.

3.2. Array and String Tips


If your DLL function must create an array, change its size, or resize a string of data
without using LabVIEW handles, then break the function into two steps. In the first
step, determine the number of elements needed in the array, or the length of the
string to be returned. Have this function return the desired size to LabVIEW. In
LabVIEW, initialize an array or string with default values, and pass this array to a
second function in your DLL, which actually places the data into the array. If you are
working with string-based instrument control, it may be easier to pass an array of 8-
bit integers than C strings because of the possibility of NULL values in the string.

Writing DLLs and LabView: (10)

Heinz Rongen Forschungszentrum Jülich, ZEL 52425 Jülich, Germany


Tel: +49-(0)2461-614512 Fax: +49-(0)2461-613990 Email: H.Rongen@fz-juelich.de
3.3. Troubleshooting the Call Library Function and Your DLL
If, after configuring the Call Library Function dialog, you still have a broken Run
arrow in LabVIEW, check to make sure that the path to the DLL file is correct. If
LabVIEW gives you the error message function not found in library, double-check the
spelling of the name of the function you wish to call. Remember that function names
are case sensitive. Also, recall that you need to declare the function with the
_declspec (dllexport) keyword in the header file and the source code or define it in
the exports section of the module definition file.
Even if you have used the _declspec (dllexport) keyword and are using the _stdcall
calling convention, then you have to declare the DLL function name in the EXPORTS
section of the module definition file. If this is not done, the function will be exported
with the mangled name, and the actual function name will be unavailable to
applications that call the DLL.
If the function has not been properly exported, you will need to recompile the DLL.
Before recompiling, make sure to close all applications and VIs that may make use of
the DLL. If the DLL is still in memory, the recompile will fail. Most compilers will warn
you if the DLL is in use by an application.
If you’ve already double-checked the name of the function, and have properly
exported the function, find out whether you have used the C or C++ compiler on the
code. If you have used the C++ compiler, the names of the functions in the DLL have
been altered by a process called “name mangling”. The easiest way to correct this is
to enclose the declarations of the functions you wish to export in your header file with
the extern “C” statement:

extern „C”
{
/* your function prototypes here */
}

After properly configuring the Call Library Function, run the VI. If it does not run
successfully, you might get errors or a General Protection Fault. If you get a General
Protection Fault, there are several possible causes. First, make sure that you are
passing exactly the parameters that the function in the DLL expects. For example,
make sure that you are passing an int16 and not an int32 when the function expects
int16. Also confirm that you are using the correct calling convention _stdcall or C.

Calling the DLL from another C program is also an excellent way to debug your DLL.
By doing this, you have a means of testing your DLL independent of LabVIEW, thus
helping you identify possible problems sooner.

Writing DLLs and LabView: (11)

Heinz Rongen Forschungszentrum Jülich, ZEL 52425 Jülich, Germany


Tel: +49-(0)2461-614512 Fax: +49-(0)2461-613990 Email: H.Rongen@fz-juelich.de
3.4. Important Reminders and Quick Reference

ü Make sure you use the proper calling convention (C or stdcall).


ü Know the correct order of the arguments passed to the function.
ü NEVER resize arrays or concatenate strings using the arguments passed directly
to a function. Remember, the parameters you pass are LabVIEW data. Changing
array or string sizes may result in a crash by overwriting other data stored in
LabVIEW memory. You MAY resize arrays or concatenate strings if you pass a
LabVIEW Array Handle or LabVIEW String Handle and are using the Visual C++
compiler or Symantec compiler to compile your DLL.
ü When passing strings to a function, remember to select the correct type of string
to pass – C or Pascal or LabVIEW string Handle.
ü Remember, Pascal strings are limited to 255 characters in length.
ü Remember, C strings are NULL terminated. If your DLL function returns numeric
data in a binary string format (for example, via GPIB or the serial port), it may
return NULL values as part of the data string. In such cases, passing arrays of
short (8-bit) integers is most reliable.
ü If you are working with arrays or strings of data, ALWAYS pass a buffer or array
that is large enough to hold any results placed in the buffer by the function unless
you are passing them as LabVIEW handles, in which case you can resize them
using CIN functions under Visual C++ or Symantec compiler.
ü Remember to list DLL functions in the EXPORTS section of the module definition
file if you are using _stdcall.
ü Remember to list DLL functions that other applications call in the module
definition file EXPORTS section or to include the _declspec (dllexport) keyword in
the function declaration.
ü If you use a C++ compiler, remember to export functions with the extern “C”{}
statement in your header file in order to prevent name mangling.
ü If you are writing your own DLL, you should not recompile a DLL while the DLL is
loaded into memory by another application (for example, your VI). Before
recompiling a DLL, make sure that all applications making use of the DLL are
unloaded from memory. This ensures that the DLL itself is not loaded into
memory. You may fail to rebuild correctly if you forget this and your compiler does
not warn you.
ü Test your DLLs with another program to ensure that the function (and the DLL)
behave correctly. Testing it with the debugger of your compiler or a simple C
program in which you can call a function in a DLL will help you identify whether
possible difficulties are inherent to the DLL or LabVIEW related.

Writing DLLs and LabView: (12)

Heinz Rongen Forschungszentrum Jülich, ZEL 52425 Jülich, Germany


Tel: +49-(0)2461-614512 Fax: +49-(0)2461-613990 Email: H.Rongen@fz-juelich.de
3.5. Datatypes
LabView Description Bytes C Type
char character 1 char
U8 unsigned 8 bit Integer 1 unsigned char
U16 unsigned 16 bit Integer 2 unsigned short
U32 unsigned 32 bit Integer 4 unsigned long
I8 signed 8 bit Integer 1 signed char
I16 signed 16 bit Integer 2 signed short
I32 signed 32 bit Integer 4 signed long
SGL single precision float 4 float
DBL double precision float 8 double

Writing DLLs and LabView: (13)

Heinz Rongen Forschungszentrum Jülich, ZEL 52425 Jülich, Germany


Tel: +49-(0)2461-614512 Fax: +49-(0)2461-613990 Email: H.Rongen@fz-juelich.de
4. Hardware access:

PC add on boards are either I/O or Memory mapped into the System address space.
The PC uses 32 bit addresses for memory and 16 bit addresses for I/O. For I/O this
means we have maximum 65536 I/O addresses (0..FFFF). The ISA Bus only
decodes the lower 10 address lines, resulting in a I/O address range from 0..3FF
(1024 I/O addresses) . PCI devices can use the full I/O address range up to FFFF.

Access to the hardware is done with a normal memory access instructions for
memory mapped add on board, or with port I/O instructions for I/O mapped devices.

Example: Write/Read from Memory/Port address 0x1234:

memory mapped I/O mapped


short *ptr
ptr = 0x1234;
*ptr = 0x55; outw (0x1234, 0x55)

x = *ptr; x = inpw (0x1234)

4.1. Hardware access on different Windows Platforms

We have to decide between 16 bit and 32 bit Windows Operating Systems:

Win 3.11 Win 95 Win 98 Win-NT 4 Win 2000

16 bit OS

32 bit OS

With 16 bit Windows-OS you have full access to all I/O and memory mapped devices
from the Application layer. (DLL running at application level)
In 32 bit Windows-OS hardware access is only possible with a “Kernel Mode Device
Driver”.

Writing DLLs and LabView: (14)

Heinz Rongen Forschungszentrum Jülich, ZEL 52425 Jülich, Germany


Tel: +49-(0)2461-614512 Fax: +49-(0)2461-613990 Email: H.Rongen@fz-juelich.de
Assembler Routines for Port I/O:
void outp (WORD adr, BYTE data) void outpw (WORD adr, WORD data)
{ {
_DX = adr; _DX = adr;
_AL = data; _EAX = data;
__emit__ (0xEE); __emit__ (0x66, 0xEF);
} }

BYTE inp (WORD adr) WORD inpw (WORD adr)


{ {
_DX = adr; _DX = adr;
__emit__ (0xEC); __emit__ (0x66, 0xED);
return (_AL); return (_AX);
}
}

Writing DLLs and LabView: (15)

Heinz Rongen Forschungszentrum Jülich, ZEL 52425 Jülich, Germany


Tel: +49-(0)2461-614512 Fax: +49-(0)2461-613990 Email: H.Rongen@fz-juelich.de
PCI Devices in an ISA World

Abstract
ISA add-in cards use IO address aliasing to get around the limited IO space available
in standard PC-compatibles. This aliasing (and the dependence upon it) means that
PCI devices must never require more than 256 bytes of contiguous IO space.

Introduction
This paper describes what ISA aliasing is and how it came into existence. Once ISA
aliasing is understood, the obvious requirements on PCI devices are discussed.
This paper is motivated by encounters with PCI devices that have required more than
256 bytes of IO space. These devices will cause problems in systems also having an
ISA bus. How the problem shows up depends on the particular system, but
symptoms include:
♦ the BIOS is unable to initialize the PCI device
(because the BIOS will not allocate an IO space larger than 256 bytes)
♦ ISA devices (that use IO aliasing) quit working,
♦ or won't work when the PCI device is in the system.

ISA Aliasing
Many years ago, when IBM first introduced its' "PC", the architecture for that system
was such that motherboard devices only decoded the lower ten address lines for IO
space addresses This effectively limited the IO space of these early PCs to 1K bytes
(210). The system design further mandated that of these 1K bytes of IO ports, the
first 256 (addresses 0 thru 255) were reserved for motherboard devices, and the rest
(addresses 256 thru 1023) were available for add-in devices.

The Figure below shows how 16-bit IO addresses were decoded in these early
systems.
15 10 9 8 7 0
xxxxxx

selects particular IO port


(motherboard or add-in)
00 = motherboard device
01,10,11 = add-in device

don't cares (not decoded)

Some years later, the motherboard standard changed such that the upper six bits of
the IO address were no longer don't-cares. Motherboard devices then did a full 16-
bit decode and still tended to exist in the first 256 bytes of the IO space. This should
have allowed the full IO space (64K) to be used. However, by this time there were
many add-in cards that only did a 10-bit decode, so while motherboards were doing
a full 16-bit decode, add-in cards still only looked at the bottom ten bits. This
effectively fragmented the IO address space such that for each 1K (210) chunk of IO
space the bottom 256 bytes are available for full 16-bit decode while the upper 768
bytes are unusable because ISA cards are doing only a 10-bit decode.
Writing DLLs and LabView: (16)

Heinz Rongen Forschungszentrum Jülich, ZEL 52425 Jülich, Germany


Tel: +49-(0)2461-614512 Fax: +49-(0)2461-613990 Email: H.Rongen@fz-juelich.de
The Figure shows the fragmented IO address space.
Full IO Space
Sample 4K Chunk

10-bit decode 0xF000


0xXD00
0xXC00 16-bit decode
0xE000

10-bit decode
0xX900 0xD000
0xX800 16-bit decode

10-bit decode
0xX500
0xX400 16-bit decode 0x2000

10-bit decode
0xX100 0x1000
0xX000 16-bit decode

0x0000

This fragmentation essentially left ISA add-in cards a total of 768 IO locations to
perform whatever functions they wanted to perform. The sheer numbers of add-in
cards led to compatibility problems because two ISA cards would end up claiming the
same IO addresses leading to one (or both) cards not working. Some defacto
standards arose where certain classes of devices got certain IO locations, but these
have not been sufficient for some functions.
Many ISA add-in cards have gotten around this limitation by claiming a small number
of locations in the 10-bit range, and then using 16-bit aliases of those location to map
other device registers. For instance, if an ISA device uses the 10-bit address 0x300,
it can also use the 16-bit aliases of that address (0x1300, 0x2300, ..., 0x0700,
0x1700, 0x2700, ..., 0x0B00, 0x1B00, 0x2B00, ..., 0x0F00, 0x1F00, 0x2F00, ...). For
every IO location in the 10-bit decode area, there are 63 16-bit aliases of that
location.

Writing DLLs and LabView: (17)

Heinz Rongen Forschungszentrum Jülich, ZEL 52425 Jülich, Germany


Tel: +49-(0)2461-614512 Fax: +49-(0)2461-613990 Email: H.Rongen@fz-juelich.de
PCI Device Requirements
How does this ISA aliasing effect PCI devices? Let's take as an example a fictional
PCI device that for some reason is built so that it requires 4K of IO space. This IO
space is requested and allocated using one of the Base Address registers in the
device's configuration space. Let's assume that the BIOS assigns the PCI device 4K
IO locations beginning at 0x4000. Any IO accesses that the processor makes with
an address of 0x4XXX will be claimed by the PCI device. But an ISA card may also
be expecting to use IO locations in this range. Typically this will mean that the ISA
card will not function in a system with this PCI device.
This leads to the a basic rule for PCI devices if they want to reliably operate in
systems containing an ISA bus. PCI devices must not require more than 256-
bytes of contiguous IO space. This requirement implies that Base Registers
requesting IO space must implement writable bits in bit locations 31::8. If a smaller
(than 256) amount of IO space is required then more writable bits should be
implemented (eg. bits 31::4 for a 16 byte area).

Writing DLLs and LabView: (18)

Heinz Rongen Forschungszentrum Jülich, ZEL 52425 Jülich, Germany


Tel: +49-(0)2461-614512 Fax: +49-(0)2461-613990 Email: H.Rongen@fz-juelich.de

You might also like