Sunteți pe pagina 1din 18
Writing DLLs and calling DLLs from LabView © by Heinz Rongen Forschungszentrum Jülich Zentrallabor für

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)

1. What are DLLs ? 3 1. The Anatomy of a DLL 3 1.1. Outline

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:

4.1. Hardware access on different Windows Platforms:

Writing DLLs and LabView: (2)

14

14

1. What are DLLs ? A Dynamic Link Library (DLL) is a file of code,

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)

1.1. Outline of the Source Code of a DLL The following example code illustrates the

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++:

in Microsoft Visual C/C++:

DllEntryPoint

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)

Finally, a DLL also contains functions that perform the activities the DLL expects to accomplish.

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

return (c);

= a + b;

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)

/* ourdll.c source code */ #include <windows.h> #include "ourdll.h" DWORD WINAPI Func1 (DWORD a, DWORD

/* ourdll.c source code */ #include <windows.h> #include "ourdll.h"

DWORD WINAPI

Func1 (DWORD a, DWORD b)

{

DWORD c;

c

return (c);

= a + b;

}

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)

2.2. The Header File The following code lists the header file ourdll.h. All of the

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)

3. Calling the DLL from LabView This simple DLL can now be called by using

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.

diagram and configure it to call the avg_num function. As specified in the previous diagram, first

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)

After making the appropriate connections, run the VI. Congratulations! You’ve written, compiled, and linked to

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)

characters. A Pascal string that is n characters long will require n + 1 bytes

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)

3.3. Troubleshooting the Call Library Function and Your DLL If, after configuring the Call Library

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)

3.4. Important Reminders and Quick Reference ¸ Make sure you use the proper calling convention

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)

3.5. Datatypes LabView Description Bytes C Type char character 1 char U8 unsigned 8 bit

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)

4. Hardware access: PC add on boards are either I/O or Memory mapped into the

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

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.

means we have maximum 65536 I/O addresses (0

FFFF).

The

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

16 bit OS

16 bit OS
16 bit OS
16 bit OS

Win-NT 4

Win 2000

32 bit OS

32 bit OS
32 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)

Assembler Routines for Port I/O: void outp (WORD adr, BYTE data) void outpw (WORD adr,

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; emit

return (_AL);

(0xEC);

_DX = adr; emit

return (_AX);

(0x66, 0xED);

}

 

}

Writing DLLs and LabView: (15)

PCI Devices in an ISA World Abstract ISA add-in cards use IO address aliasing to

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 (2 10 ). 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
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 (2 10 ) 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)

The Figure shows the fragmented IO address space. Sample 4K Chunk 0xXD00 0xXC00 0xX900 0xX800

The Figure shows the fragmented IO address space.

Sample 4K Chunk

0xXD00

0xXC00

0xX900

0xX800

0xX500

0xX400

0xX100

0xX000

10-bit decode

16-bit decode

10-bit decode

16-bit decode

10-bit decode

16-bit decode

10-bit decode

16-bit decode

Full IO Space 0xF000 0xE000 0xD000 0x2000 0x1000 0x0000
Full IO Space
0xF000
0xE000
0xD000
0x2000
0x1000
0x0000

This fragmentation essentially left ISA add-in cards a total of 768 IO locations to

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

perform whatever functions they wanted to perform.

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,

, every IO location in the 10-bit decode area, there are 63 16-bit aliases of that

location.

0x1700, 0x2700,

, 0x0700,

).

For

0x0B00, 0x1B00, 0x2B00,

,

0x0F00, 0x1F00, 0x2F00,

Writing DLLs and LabView: (17)

PCI Device Requirements How does this ISA aliasing effect PCI devices? Let's take as an

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)