Documentation

Overview

The information provided here is limited to the minimum. More information (e.g.: introductions, description of parallel port etc.) can be found on following sites:

Site Comment
The EE Compendium Data tables and many links
Beyond Logic Description of the parallel port and how to use it

Port Addresses

Name Base Address Alternative Base Address
LPT1 0x0378 0x03BC
LPT2 0x0278 0x0378
LPT3 0x03BC None

Port Register

Register Direction Address Mask
Data Register Input/Output Base Address 0xFF
Status Register Input Base Address + 1 0xF8
Control Register Output Base Address + 2 0x0F

Remarks:

  • Other registers ( e.g.: ECP or EPP ) are not used by ParaPort.
  • The mask is defined by binary OR on the 'Bit Mask' value in the following table.
  • The parallel port has 8 bit input and output, 5 bit input and 4 bit output.

Port Register and Connector Pins

Register Bit Number Bit Mask Functionality Name Inverted Pin
Data Register 0 0x01 Bit 0 of data byte No 2
Data Register 1 0x02 Bit 1 of data byte No 3
Data Register 2 0x04 Bit 2 of data byte No 4
Data Register 3 0x08 Bit 3 of data byte No 5
Data Register 4 0x10 Bit 4 of data byte No 6
Data Register 5 0x20 Bit 5 of data byte No 7
Data Register 6 0x40 Bit 6 of data byte No 8
Data Register 7 0x80 Bit 7 of data byte No 9
Status Register 3 0x08 Error No 15
Status Register 4 0x10 Select No 13
Status Register 5 0x20 PaperEnd No 12
Status Register 6 0x40 Acknowledge No 10
Status Register 7 0x80 _Busy Yes 11
Control Register 0 0x01 _Strobe Yes 1
Control Register 1 0x02 _LineFeed Yes 14
Control Register 2 0x04 Initialize No 16
Control Register 3 0x08 _SelectIn Yes 17

Remarks:

  • Some Pins are inverted on the connector. To set the pin to logical high TTL ( 5 Volt ), you must clear the correspondent bit. To show this, the functionality name starts with an underscore.

Global Operating Model

To use ParaPort, its driver must first be installed by the Windows "Add/Remove Hardware Wizard".

Client applications ( executables ) do not access directly the driver, but use the Application Programming Interface ( "API" ) of the "ParaPort.dll" dynamic library. They generally follows these steps:

  • load the "ParaPort.dll"
  • use functionality "open a port" to get a HANDLE ( in the sense of Microsoft's Windows API ) to a port
  • if needed, get information about the port
  • write data to or read data from the port by "executing a cycle" or
  • close the port, when not needed anymore

The ParaPortUtility application uses the underlying dll and driver to set or clear pins of the parallel port.

System Architecture

Data Model

The central information item of ParaPort is a "cycle". This is a structure ( in the sense of C or C++ ) containing seven bytes:

A special direction bit ( 0x20 ) of the control register determines whether the data register is used as output ( bit cleared ) or input ( bit set ).

As example:

  • To set the pin 7 of the parallel port to logical high ( 5 Volt TTL ), the structure member PARAPORT_CYCLE::Data must be set to 0x20 ( in comparison with the data table ). PARAPORT_CYCLE::MaskData must also set to 0x20.
  • To clear the pin 7 ( logical low = 0 Volt TTL ), PARAPORT_CYCLE::Data must be set to 0x00. PARAPORT_CYCLE::MaskData must set to 0x20.

Timing Model

Each cycle is seen as one moment in time. But since there are CPU instructions to execute on the PC and electronic reaction on parallel port hardware, the following timing diagrams can be measured on the parallel port:

  • if the direction bit 0x20 of the control register is cleared ( data register is output ):
    1. configure data register as output
    2. write data register ( * )
    3. write control register ( * )
    4. apply the "repeat factor" to waste time ( to enable connected hardware to react )
    5. read status register ( * )
  • if the direction bit 0x20 of the control register is set ( data register is input ):
    1. configure data register as input
    2. write control register ( * )
    3. apply the "repeat factor" to waste time ( to enable connected hardware to react )
    4. read data register ( * )
    5. read status register ( * )

All register are written or read ( the lines marked with "*" ) only, if the correspondent mask byte is not equal to 0x00.

Repeat Factor

The following figure has been measured with a logical analyser during the execution of the sample application "sampleRepeatFactor". As you can see in the source code, different pulses has been generated using each two cycles.

Each cycle lasts approximate 5 micro seconds. By setting the byte PARAPORT_CYCLE::RepeatFactor in the structure, a cycle can be extended ( e.g.: cycles 3, 5, 7 ).

To extend the time between pulses, the cycles 2, 4, 6 should get a value > 0 for the byte PARAPORT_CYCLE::RepeatFactor.

Pulsed or Clocked Input / Output

During one cycle, the state of the pins can be set, cleared or read. To realize "Pulsed or Clocked I/0", two cycles are needed for each pulse, if the hardware is "edge driven". In other cases, three cycles are needed.

As example 1:

To write 0xAE on the data register with a clock (e.g.: _Strobe), two cycles are needed:

PARAPORT_CYCLE Cycle 1 Cycle 2
Control 0x01 0x00
MaskControl 0x21 0x01
Data 0xAE 0x00
MaskData 0xFF 0xFF
  • Cycle 1 configures the data register as output and puts 0xAF on the parallel port. Then the _Strobe signal line is triggered and the repeat factor is applied.
  • Cycle 2 resets the _Strobe signal line and puts into the data register 0x00 as the default value on idle time. If an other value ( e.g.: 0xFF ) is wanted, Data must be changed; if the last set value ( here 0xAE ) is wanted, the MaskData must set to 0x00. If the default state of the data register is input, the bit 0x20 must be set in the Control structure member.

As example 2:

To read a byte from the data register with a clock (e.g.: _Strobe), two cycles are needed:

PARAPORT_CYCLE Cycle 1 Cycle 2
Control 0x21 0x00
MaskControl 0x21 0x01
Data read value 0x00
MaskData 0xFF 0xFF
  • Cycle 1 configures the data register as input, the _Strobe signal line is triggered and the repeat factor is applied. Then the data register will be read and the value will be stored in the Data structure member.
  • Cycle 2 resets the _Strobe signal line and puts into the data register ( here, its default state is "output" ) 0x00 as the default value on idle time. If an other value ( e.g.: 0xFF ) is wanted, Data must be changed; if the last set value ( here 0xAE ) is wanted, the MaskData must set to 0x00. If the default state of the data register is input, the bit 0x20 must be set in the Control structure member.

Overview

ParaPort offers to its client applications written in C or C++ an Application Programming Interface ( API ) for the exported functions of ParaPort.dll.

The C interface of ParaPort is implemented in the header file "ParaPort.h" and presents all C type definitions and function prototypes needed to access the dll.

The C++ interface of ParaPort contains C++ classes which wraps the C functions and C structures:

  • The class ParaPortCycle in header file "ParaPortCycle.h" proposes "get" / "set" access methods to the members of the C structure PARAPORTCYCLE.
  • The class ParaPortDll in header file "ParaPortDll.h" does the standard work of a DLL client to load the DLL ( ::LoadLibrary( ) ), determine the addresses of the DLL exported functions ( ::GetProcAddress( ) ) etc.

The usage of ParaPort can be viewed in source code of the sample program.

Software Architecture

API "open a port"

Description:

Before writing data to or reading data from the parallel port using ParaPortCycle, the client application must get a handle from ParaPort to a port by specifying its name ( e.g.: "LPT1" ). This handle will be used to call the other methods.

Method:

HANDLE openPort( const char* PortName );

Parameter:

Type Parameter Description
const char* PortName Not case sensitive character array with name of port "LPT1" to "LPT3". Some computers support "LPT4" or more. Valid values are e.g.: "LPT1", "Lpt2", "lpt3", "lpT4" etc.

Return value:

Type Possible Values Description
HANDLE INVALID_HANDLE_VALUE Error occurred, call ::GetLastError() from Windows API to get information about the error
  all other values a valid handle ( in the sense of Windows API ), which can be used as parameter of the other methods

API "get information about a port"

Description:

The following information can be extracted from a port:

  • the port address
  • the port mode

API "execute cycle"

Description:

This is the central functionality to write data to or read data from the parallel port.

Types:


typedef struct {
  UCHAR Reserved;
  UCHAR Data;
  UCHAR Status;
  UCHAR Control;
  UCHAR MaskData;
  UCHAR MaskStatus;
  UCHAR MaskControl;
  UCHAR RepeatFactor;
} PARAPORT_CYCLE;
      

Method:

BOOL executeCycle( HANDLE Handle, PARAPORT_CYCLE* ParaPortCycle, int Count );

Parameter:

Type Parameter Description
HANDLE Handle handle of the port ( returned by openPort( ) )
PARAPORT_CYCLE* ParaPortCycle array of cycles
int Count number of cycles in the array

Return value:

Type Possible Values Description
BOOL TRUE Method was executed successfully
  FALSE Error occurred, call ::GetLastError() from Windows API to get information about the error

API "direct register access"

Description:

This functionality gives direct access to the parallel port registers. These functions have the __stdcall declarators, so they can be called from Visual Basic applications.

Types:

PARAPORT_ADDRESS

Methods:

PARAPORT_ADDRESS getPortAddress( const char* PortName );
UCHAR input( PARAPORT_ADDRESS Address );
UCHAR output( PARAPORT_ADDRESS Address, UCHAR Byte ); 

Parameter:

Type Parameter Description
const char* PortName Not case sensitive character array with name of port "LPT1" to "LPT3". Some computers support "LPT4" or more. Valid values are e.g.: "LPT1", "Lpt2", "lpt3", "lpT4" etc.
PARAPORT_ADDRESS Address address of the port ( returned by getPortAddress( ) )
UCHAR Byte byte to be written

Return value:

Type Possible Values Description
PARAPORT_ADDRESS Address address of the port
UCHAR byte to be read Method was executed successfully
  PARAPORT_BYTE_ON_ERROR Error occurred, call ::GetLastError() from Windows API to get information about the error

API "close a port"

Description:

If a port is no more accessed, the client application must close the handle of the port.

Method:

BOOL closePort( HANDLE Handle );

Parameter:

Type Parameter Description
HANDLE Handle handle of the port ( returned by openPort( ) )

Return value:

Type Possible Values Description
BOOL TRUE Method was executed successfully
  FALSE Error occurred, call ::GetLastError() from Windows API to get information about the error

API "error handling"

Description:

All methods of ParaPort uses the error handling mechanism of Windows by using the functions ::SetLastError( ) and ::GetLastError( ) from Windows API. ParaPort specific errors are C defines in the custom range beyond 0x20000000. There are also some common Windows errors returned by the functions.

ParaPort specific defines:

PARAPORT_ERROR   start of the ParaPort error range
PARAPORT_ERROR_INTERNAL_1 all functions internal error, should never arrive
PARAPORT_ERROR_INTERNAL_2 all functions internal error, should never arrive
PARAPORT_ERROR_INTERNAL_3 all functions internal error, should never arrive
PARAPORT_ERROR_INVALID_HANDLE all functions the handle given as parameter is invalid
PARAPORT_ERROR_INVALID_PORTNAME openPort( ) or getPortAddress( ) the name of the port is invalid
PARAPORT_ERROR_LIBRARY_NOT_IMPLEMENTED all functions this functionality is not ( yet ) implemented
PARAPORT_ERROR_LIBRARY_NOT_LOADED all functions  
PARAPORT_BYTE_ON_ERROR input( ) or output( ) the address of the port
PARAPORT_ERROR_INVALID_CYCLE executeCycle( ) ParaPortCycle == NULL

Windows errors:

ERROR_NOT_ENOUGH_MEMORY executeCycle( ) returned, if memory allocation fails in case of limited memory resources on the computer

API "dll singleton"

Description:

The C++ interface implements the singleton design pattern for the class ParaPortDll.

The client application normally uses only one instance of the class, which is created by the first call of the static method getSingleton( ). Following calls of this method do not create any new objects, but return the address of the instance created by the first call.

If the object is no longer needed, it can be deleted by calling the method deleteSingleton( ).

Method:

static ParaPortDll* getSingleton( );
static void deleteSingleton( );

Driver "ParaPort.sys"

"ParaPort.sys" is a Windows kernel driver following Microsoft's Windows Driver Model (WDM). The driver can be controlled ( start, stop, install, uninstall ) via the "Device Manager" of the computer.

Application "ParaPortUtility.exe"

ParaportUtility is a simple dialog box based windows application which can be used to set or clear pins on parallel port connectors. To be operational, the application needs the driver "ParaPort.sys" being installed and the "ParaPort.dll" must be visible to "ParaPortUtility.exe".

The Graphical User Interface consists of the following sections:

  • "Port Parameter": select the parallel port. The detected ports of the computer are displayed in the combo box.
  • "Data Register": read ( "Input" ) or write ( "Output" ) a byte on the parallel port. The byte can be specified as hexadecimal value or with check boxes.
  • "Status Register" displaying the state of the 5 input lines.
  • "Control Register": set or clear the 4 output control lines.
  • On the bottom of the dialog box, there is one button "Execute Cycle" to transfer Data to/from the parallel port and a second to exit the application.

A checked box in the "Data Register", "Status Register" or "Control Register" ( e.g.: like "Pin9: D7" ) means that the correspondent pin on the parallel port connector has 5 Volt TTL. An unchecked box means 0 Volt TTL.

Frequently Asked Questions

FAQ General

ParaPort ( in its current releases ) is not compatible with Windows NT4. The reason is historical:

  • Once upon a time, Microsoft invented Windows 95, which used "virtual device drivers".
  • Then Windows NT4 was released using a completely new system of drivers known as "NT4 drivers".
  • Later, Windows 98 ( SE, ME ) came introducing the "Windows Driver Model", ( WDM ) which is an enhancement ( addition of "Plug 'n' Play" etc. ) of the NT4 model. But these new drivers were no more compatible with ( and executable on ) Windows NT4!
  • Windows 2000 and Windows XP are build on the same "Windows Driver Model" of Windows 98, but for compatibility purposes can execute and use NT4 drivers.
  • ParaPort is a WDM driver and so applicable to Windows 98 ( SE, ME ), Windows 2000 and Windows XP. To use on Windows NT4, ParaPort must be rewritten as "NT4 driver", which is technically possible and but does not make sense because Windows NT4 is obsolete.

There will be an "direct access" API (simple inp( ) and outp( ) in release v2.0 of ParaPort.

ATTENTION: If you get a eMail from "contact@paraport.net" with the following informations:

  • Subject "Re: Application", "Re: Movie", or "Re: Submited"
  • Message text: "Please see the attached zip file for details." or "Please see the attached file.".
  • Attachment: "UNKNOWN_PARAMETER_VALUE application/x-zip-compressed 109.84 KB" or "UNKNOWN_PARAMETER_VALUE application/octet-stream 77.32 KB"

you MUST DELETE THE MESSAGE without executing the attached file! Executing the attached file will infect your Windows system with the virus "W32.Sobig.E@mm".

More information about the virus can be found on Symantec .

FAQ about "ParaPort.dll"

The library of ParaPort has the form of a "*.dll" file because of the following advantages:

  • Distributing the ParaPort library as static linked library "ParaPort.lib" generates problems with different compiler options between library file and client application ( e.g.: library in single thread, application in multi thread model ).
  • Using a "*.dll" file by linking the corresponding "*.lib" file let Windows search and load the "*.dll" file at process start. In case of error, a standard message box is displayed and the programmer has no possibility to customize the error handling.
  • A "*.lib file" cannot be used by other development environments ( e.g.: Borland C, Builder, Visual Basic ).

My implementation of the Singleton is an extension of the singleton published by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides. The Problem of their implementation is that they did not preview to delete of the allocated memory ( even at end of the process ). Some development utilities verifying, that all allocated memory is also freed, will signal memory leaks at the end of the process.

To read data or status register from the parallel port, the corresponding mask members of the PARAPORT_CYCLE structure must be set: The following source code reads the bit 2, 3 and 4 of data register ( to read the howl byte, put 0xFF into variable MaskData ) and the "PaperEnd" bit of the status register.

ParaPortCycle Cycle[ 1 ];
UCHAR MaskData =   PARAPORT_MASK_DATA_D2
                 | PARAPORT_MASK_DATA_D3
                 | PARAPORT_MASK_DATA_D4;
Cycle[ 0 ].setMaskStatus( PARAPORT_MASK_STATUS_PAPEREND );
Cycle[ 0 ].setDataInput( );
Cycle[ 0 ].setMaskData( MaskData );
Dll->executeCycle( Handle, Cycle, 1 );
UCHAR Data = Cycle[ 0 ].getData( );
bool PaperEnd = Cycle[ 0 ].isStatusPaperEnd( );

A sample "read cycle" will be added in release v2.0 to show this.

Please have a look to the source code of the samples. The path of the library file is constant to simplify the source code and may be adapted to the customers target directory structure. In a "well programmed" application, the file name should be variable ( e.g.: configuration file, command line parameter ).

To use ParaPort in a MFC Application ( the ParaPortUtility application is one ), you should

  • call the loadLibrary( ) method in the InitInstance( ) method of the application's main class derived from CWinApp,
  • call the deleteSingleton( ) method in the ExitInstance( ) method of the same class,
  • create a class member HANDLE m_Handle; of an appropriated class ( e.g.: a class derived from CDocument or CDialog ).
  • This class must first call the methods openPort( ) and than can access to the parallel port with function executeCycle( ).
  • Do not forget to call closePort( ) in the class destructor.

A simple MFC sample application will be added in release v2.0.

The release v1.3 cannot be used by Visual Basic programs. In release v2.0, the DLL interface will be changed to support Visual Basic.

There is no extra support ( e.g.: classes ) of the .NET framework previewed. There should be some techniques integrated into C# to access a simple C dynamic link library as ParaPort.dll is.

FAQ about the driver "ParaPort.sys"

ParaPort does not support the interrupt ( bit 4 of Control Register ) to enable the IRQ via the Acknowledge line ( bit 6 of Status Register ), since it was not yet required. The only workaround is continuous polling on the parallel port.

FAQ about "ParaPortUtility"

none ( ? )

Copyright 2002 - 2017 by Paul ADAM, read the Terms of Use