FASTIO with WFASTIO$ and support .DLL

DISCLAIMER: This sample code is for your information only. Do not attempt to use this code on your equipment without first inspecting the source code and making sure it does what you want. This code is supplied as is without warranty or liability of any kind.

WFASTIO$
I needed a fast way of being able to do fastio to I/O ports, to access a data acquisition card. I saw the article from EDM/2 detailing the fastio driver. This seemed to be the ticket. But, I needed to change the functionality of the driver to meet the specific needs of my D/A card. I did not have cl or MASM and could not get them because they are not available any more. I wanted to be able to use what I had (WATCOM) to be able to make the changes to the driver. The first step was then to port the fastio driver to WATCOM. That is what I did.

With this driver you can access any I/O port you want very quickly. It does so buy calling a r3->r0 call gate which is returned to the application by the device driver. The call gate goes directly to the ring 0 code and executes the in/out instructions; therefore there is no need for a ring 2 IOPL segment. The driver call gate is faster than the standard IOPL r3->r2 call gate. For one there is no stack to copy the iolib passes data in registers. For more details see Holger's Article from EDM which explains the driver (one page back).

Why wouldn't the code work as it was? Well there are two reasons.

1) MASM and WASM are not exactly the same, parts of the assembler source were required to be changed so that they would be compatible with WATCOM.

2) The default calling conventions for WATCOM are different than those for MICORSOFT so the header detailing the prototyping was changed to make WATCOM adhere to C calling conventions. See IOLIB.H for more details on the syntax of the pragma statements.

The code should be complete. What is included are the files for the device driver itself, actually modified from the hello world files, the proper ASM files with all the changes needed for WASM, and an example which uses the driver and reads from the VGA registers on your video card.

Be sure your compile using STACK CALLING CONVENTIONS (page 9 of compiler options) or the example will not work. If you need to change this option you should get a unresolved external error when compiling the code.

Get WFASTIO$

WIOLIBDLL- SUPPORT FOR WFASTIO$
Hello, and welcome. Before using this software there are a few things you should understand. This software should be used to access those devices which you would like to use but do not need to write a device driver for i.e. things like Data Acquisition cards. Be forewarned, that the I/O method used in this package does not have any checks. Having no restriction means that you can write to any port you want including video cards, sound cards etc. Wherever possible you should try to use the OS/2 system API calls to access the above device's. I am not responsible for damage or crashes to the users system as a result of these actions.

This package requires you to first load the WFASTIO$ device driver. For best results, put iolibdll.dll in C:\os2\dll. This way you only need one copy of it. Then link in the lib, and include the header with your application and you can call any of the functions in the DLL to access your device.

If you have any questions or suggestions for this package, please feel free to e-mail me alger@avenger.mri.psu.edu. As far as compilers to use it with all should work, and I myself have used it with both Watcom and VAC++.

Get WIOLIBDLL.DLL - the support .DLL

/DEV/FASTIO$ - the Final Way
Okay. We just managed to get a transforming (32->16bit) call gate, that just happens to point to the wrong address. It was a matter of seconds to find the address of the corresponding GDT entry, and redirect it to the expected position. A kernel debugger is really a neat tool for the hacker. It worked!

At this point, calling the DevHlp_DynamicAPI function becomes useless, and will just occupy a later unusable entry point in the kernel. A quick look into the list of device helper functions offers the function DevHlp_AllocGDTSelector. We acquire a default GDT selector for exclusive use by the driver, and "adjust" it to form a 32->16 bit R3->R0 call gate into the I/O routine section of the driver.

Have a look at the code fragment in the FASTIO$ driver (figure 4) which does it all. Figure 4: Initialization routine of FASTIO$ driver

Since a device driver is initialized in ring 3, this routine does not work during startup. Rather, the driver will call this code once the first time some client opens the device. Thus, to use the driver, a small routine io_init needs to be called first. Refer to the file iolib.asm that comes with this issue of EDM/2.

A final improvement: Usually, C code passes arguments on the stack. A call gate can be configured to copy these parameters over to the new ring. But why should we do this? For really fast I/O access we pass the data in registers. This allows for direct replacement of I/O instructions in assembler code by a simple indirect call as shown in figure 5. The address of the indirect call is set up by the above mentioned io_init procedure. Figure 5: Calling I/O from assembler

If the code needs to be called from C, we simply write a small stub that wraps a stack frame envelope around it, just as shown in figure 6. Figure 6: A C callable I/O function

The file iolib.asm contains a set of functions c_inX and c_outX for using I/O from any 32 bit compiler that supports the standard stack frame. The files iolib.a and iolib.lib are pre-compiled versions; the file iolib.h contains the C prototypes.

In the complete driver, I gave up a small amount of the theoretically reachable performance. There are six basic I/O operations: IN and OUT instructions exist for transferring bytes, 16 bit words and 32 bit long words. To become really fast, one would have to provide a separate GDT selector for each of them. In a typical OS/2 system, this should not be a problem. However, if now everyone would start to add more routines, each with its own entry point, this resource could become rather quickly a scarce one. So I spent a function code, to be passed in the BX register, to multiplex the six functions into a single GDT selector. Refer to the io_call entry point in the fastio_a.asm driver source file.