Virtual parallel port emulator

我们两清 提交于 2019-12-05 02:26:07

Since the environment is fake anyway, i.e. you don't have an actual port to play with, you could just as well emulate the port functionality in your program.

Here's how to do it in Windows using Structured Exception Handling (SEH):

// Filename: PortEmu.c
// Compiled with Open Watcom 1.9 as: wcl386 PortEmu.c

#include <windows.h>
#include <stdio.h>
#include <conio.h>

// Port state. Holds the last value written by OUT.
// IN reads from it.
volatile UINT32 PortState = 0;

UINT32 ReadPort(UINT16 PortNumber, UINT OperandSize)
{
  UNREFERENCED_PARAMETER(PortNumber);

  switch (OperandSize)
  {
  default:
  case 8:
    return PortState & 0xFF;

  case 16:
    return PortState & 0xFFFF;

  case 32:
    return PortState;
  }
}

void WritePort(UINT16 PortNumber, UINT OperandSize, UINT32 Value)
{
  UNREFERENCED_PARAMETER(PortNumber);

  switch (OperandSize)
  {
  default:
  case 8:
    PortState = (PortState & ~0xFF) | (Value & 0xFF);
    break;

  case 16:
    PortState = (PortState & ~0xFFFF) | (Value & 0xFFFF);
    break;

  case 32:
    PortState = Value;
    break;
  }
}

// Exception filter to emulate x86 IN and OUT instructions
// in 32-bit Windows application.
int IoExceptionFilter(LPEXCEPTION_POINTERS ep)
{
  CONTEXT* c = ep->ContextRecord;
  UINT8* instr = (UINT8*)c->Eip;
  int OperandSizeIs16Bit = 0;

  switch (ep->ExceptionRecord->ExceptionCode)
  {
  case EXCEPTION_PRIV_INSTRUCTION:
    if (instr[0] == 0x66)
    {
      OperandSizeIs16Bit = 1;
      instr++;
    }

    switch (instr[0])
    {
    case 0xE4: // IN AL, imm8
      *(UINT8*)&c->Eax = ReadPort(instr[1], 8);
      c->Eip += 2 + OperandSizeIs16Bit;
      return EXCEPTION_CONTINUE_EXECUTION;

    case 0xE5: // IN (E)AX, imm8
      if (OperandSizeIs16Bit)
        *(UINT16*)&c->Eax = ReadPort(instr[1], 16);
      else
        c->Eax = ReadPort(instr[1], 32);
      c->Eip += 2 + OperandSizeIs16Bit;
      return EXCEPTION_CONTINUE_EXECUTION;

    case 0xEC: // IN AL, DX
      *(UINT8*)&c->Eax = ReadPort((UINT16)c->Edx, 8);
      c->Eip += 1 + OperandSizeIs16Bit;
      return EXCEPTION_CONTINUE_EXECUTION;

    case 0xED: // IN (E)AX, DX
      if (OperandSizeIs16Bit)
        *(UINT16*)&c->Eax = ReadPort((UINT16)c->Edx, 16);
      else
        c->Eax = ReadPort((UINT16)c->Edx, 32);
      c->Eip += 1 + OperandSizeIs16Bit;
      return EXCEPTION_CONTINUE_EXECUTION;

    case 0xE6: // OUT imm8, AL
      WritePort(instr[1], 8, (UINT8)c->Eax);
      c->Eip += 2 + OperandSizeIs16Bit;
      return EXCEPTION_CONTINUE_EXECUTION;

    case 0xE7: // OUT imm8, (E)AX
      if (OperandSizeIs16Bit)
        WritePort(instr[1], 16, (UINT16)c->Eax);
      else
        WritePort(instr[1], 32, c->Eax);
      c->Eip += 2 + OperandSizeIs16Bit;
      return EXCEPTION_CONTINUE_EXECUTION;

    case 0xEE: // OUT DX, AL
      WritePort((UINT16)c->Edx, 8, (UINT8)c->Eax);
      c->Eip += 1 + OperandSizeIs16Bit;
      return EXCEPTION_CONTINUE_EXECUTION;

    case 0xEF: // OUT DX, (E)AX
      if (OperandSizeIs16Bit)
        WritePort((UINT16)c->Edx, 16, (UINT16)c->Eax);
      else
        WritePort((UINT16)c->Edx, 32, c->Eax);
      c->Eip += 1 + OperandSizeIs16Bit;
      return EXCEPTION_CONTINUE_EXECUTION;

    default:
      return EXCEPTION_CONTINUE_SEARCH;
    }

  default:
    return EXCEPTION_CONTINUE_SEARCH;
  }
}

int main(void)
{
  __try
  {
    outp(0x278, 0x00);
    printf("portb=0x%X\n", inp(0));

    outp(0x278, 0x55);
    printf("portb=0x%X\n", inp(0));

    outp(0x278, 0xFF);
    printf("portb=0x%X\n", inp(0));

    outpw(0x278, 0xAAAA);
    printf("portw=0x%X\n", inpw(0));

    outpd(0x278, 0x12345678);
    printf("portd=0x%X\n", inpd(0));

    outpw(0x278, 0xAAAA);
    outp(0x278, 0x55);
    printf("portd=0x%X\n", inpd(0));
  }
  __except(IoExceptionFilter(GetExceptionInformation()))
  {
  }

  return 0;
}

Output:

C:\>PortEmu.exe
portb=0x0
portb=0x55
portb=0xFF
portw=0xAAAA
portd=0x12345678
portd=0x1234AA55

Just alter the implementation of ReadPort() and WritePort() to do something more useful or more in line with the printer port operation.

ObscureRobot

It looks like dosbox may not support parallel ports without patches. It also appears that virtualbox also doesn't yet support parallel ports. But even if they did support parallel ports, you'd still need something on the other end - either a debug-driver on your host operating system, or something like a USB to Parallel adapter (available at the usual retailers).

Can you say more about why you want to learn about the parallel port? It is a mostly dead interface in 2011, as you suggest. If you really want to play with low-level parallel-style I/O, you may want to look in to the Arduino platform.

I don't know of any software offhand, though I would not be surprised if Linux Wine did a good job of supporting a parallel port, though I don't know if it can be completely virtualized when lacking a physical LPT.

When having to do legacy compatibility testing, I'm always amazed out how easy it is to find a cheap old PC.

Alas, this is highly regionally-centric, but visit a local resale store or computer recycling operation. For example, in Portland, I would visit Free Geek and Goodwill and not expect to pay more than $15. If your time is worth much, this is probably more affordable than messing around with emulators and then wondering how good they are.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!