For all of these functions, CardNum is the usual AIOWDM parameter. If you only have one card then simply pass zero, otherwise use GetNumCards and QueryCardInfo to determine which card number to use.

UInt32 CSA_ISAEmu_Init(SIntNative CardNum, UInt8 DataGroup, UInt8 AddrGroup, UInt8 usReadDelay, UInt8 Flags)

Configures how the ISA emulation is to work, and initializes ISA emulation.

DataGroup: This is the index of which PPI to use for the data side, from 0 to 4 on the PCI-DIO-120. This is equivalent to offsetData and offsetDataControl, as follows:

DataGroupoffsetDataoffsetDataControl
003
147
28Bh
3ChFh
410h13h

AddrGroup: This is similar to DataGroup, except for the address side, and based on offsetAddress and offAddressControl.

usReadDelay: The delay, in microseconds, between a read being asserted onto the bus and the data being read.

Flags: For normal operation, pass 0. However, you can set the "TST" flag by passing 1 in order to use this with the TST/BEN jumper in the TST position.

Note: We noticed some TST-mode code in the original source, but not enough to actually work in TST mode; we thus infer that you're using the card in BEN mode (which is the most common), but are interested in TST mode.

Return Value: ERROR_SUCCESS if it succeeds, or a different windows error code if it fails.

UInt32 CSA_ISAEmu_OutPort(SIntNative CardNum, UInt16 Address, UInt16 Data)

Writes Data to the ISA emulation bus at the specified Address.

Note: It automatically skips changing direction if the last access was already a write; thus no separate "repeat" function is needed.

Return value: ERROR_SUCCESS if it succeeds, or a different windows error code if it fails.

UInt32 CSA_ISAEmu_InPort(SIntNative CardNum, UInt16 Address)

Reads from the ISA emulation bus at the specified Address.

Note: It automatically skips changing direction if the last access was already a read; thus no separate "repeat" function is needed.

Note: We noticed that in the original source the delay initialized by usReadDelay was not being applied in the "repeat" case, however we guessed that was because a sufficient delay was being provided by the ring transitions. Since this version eliminates the ring transition delay, we apply usReadDelay on every read.

Return value: The data read from the port if it succeeds, or 0xAA55AA55 if it fails. GetLastError can be used to retrieve the associated error code.

Example usage

Before:

bool GrantIOPortAccessToProcess()
{
	bool result = true;

#ifndef RUNWITHOUTHARDWARE
     if (InPortB(0x61) == 0xAA55)
     {
	      AfxMessageBox("ACCESNT.SYS not detected. Please copy ACCESNT.SYS into [NT]/system32/drivers and re-run this sample.",  MB_ICONEXCLAMATION);
        result = false;
     }
     else
     {
       // Address control port sends the Address to the PAL to be decoded.
       // there is no ISA bus so I emulate it via software.
       // First I initiate the Data port for output and leave it active. It won't
       // do anything because the IOW or IOR signals on the Address control port need to be lowerd
       // to activate the rs422 chips.

       RelOutPortB(0, offsetDataControl, 0x80); // input port A and port B port active
	     RelOutPortB(0, offsetDataControl, 0x00); // enable buffer for Group 0 all ports on cards with TST feature
       RelOutPortB(0, offAddressControl, 0x80); // always output
       RelOutPortB(0, offAddressControl, 0x00); // enable buffers for Group 1 all ports on cards with TST feature
       RelOutPort(0, offsetAddress, (0x1800)); // set address to 0 with IOR and IOW disabled
     }
#endif
	return result;
}

After:

bool GrantIOPortAccessToProcess()
{
	bool result = true;

#ifndef RUNWITHOUTHARDWARE
     if (InPortB(0x61) == 0xAA55)
     {
	      AfxMessageBox("ACCESNT.SYS not detected. Please copy ACCESNT.SYS into [NT]/system32/drivers and re-run this sample.",  MB_ICONEXCLAMATION);
        result = false;
     }
     else
     {
       // Address control port sends the Address to the PAL to be decoded.
       // there is no ISA bus so I emulate it via software.
       // First I initiate the Data port for output and leave it active. It won't
       // do anything because the IOW or IOR signals on the Address control port need to be lowerd
       // to activate the rs422 chips.

       CSA_ISAEmu_Init(0, offsetData / 4, offsetAddress / 4, 10, 0);
     }
#endif
	return result;
}

Before:

void OUTW(WORD port, WORD value)
{
#ifndef RUNWITHOUTHARDWARE
  RelOutPortB(0, offsetDataControl, 0x80);

  RelOutPort(0, offsetData, value );//| value2);
  RelOutPort(0, offsetAddress, (port | 0x0800));
  RelOutPort(0, offsetAddress, (port | 0x1800));// disable IOR and also IOW

#endif
}

After:

void OUTW(WORD port, WORD value)
{
#ifndef RUNWITHOUTHARDWARE
CSA_ISAEmu_OutPort(0, port, value);
#endif
}

Before:

WORD INPW(WORD port)
{
#ifndef RUNWITHOUTHARDWARE
  WORD inputVal(0);
  RelOutPortB(0, offsetDataControl, 0x12);
  RelOutPort(0, offsetAddress, (port | 0x1000 ));
  DelayMicroseconds(10);
  inputVal = RelInPort(0, offsetData);
  RelOutPort(0, offsetAddress, (port | 0x1800));// disable IOR and also IOW
  return inputVal;
#else
	return (WORD)0;
#endif
}

After:

void OUTW(WORD port, WORD value)
{
#ifndef RUNWITHOUTHARDWARE
  value = CSA_ISAEmu_InPort(0, port);
#endif
}