ほんとーに今更ですがパラレルポートのお話

# はじめに
WindowsXP向けのあるソフトを継続して使用したいが、どうにかならんのか？という話がありまして、色々調査した結果のまとめです。
このソフトでは、パラレルポートにドングルを挿してライセンス認証を行うタイプでした。
WindowsXPでも、USBタイプのライセンス用ドングルは使用されているので、当時でも結構古めのソフトだったのではないかと推測します。

# ハードウェア
パラレルポートにもいくつかの種類がありますが、今回は２５ピンのこんな形状のモノのお話です。
![Untitled Diagram.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248779/3436259d-8a48-b1d7-cf88-0a8c5cca11cc.png)
IEEE 1284として規格化されているそうです。

この画像でのピン番号の配列はメス端子を前面から見たところを図にしています。
オス端子を正面から見た際は、ピン番号の左右が反転しますのでご注意ください。

で、WikiにもありますがI/Oとして0x0378-0x037Fのアドレス範囲を取ります。
マザーボードに載っているパラレルポートですとこの固定アドレスを使用するのですがPCIなどの拡張カードで増設するパラレルポートですと、遠慮してかこれとは違うアドレス範囲を取ることがあるようで、今回ワタシが困ったのがこの点でした。
（私が使用したカードはアドレスが0xD050-0xD052でした）

というのも、使用しているソフトが、0x0378-0x037Fの固定アドレスを使用してドングルとインターフェースしているらしくオンボードにパラレルポートを持ったWindowsXP対応のマザーボードが手に入らなければアウトとなります。

I/Oアドレスとピンの関係

| アドレス  |MSB|   |   |   |   |   |   |LSB|
|:---------|---|---|---|---|---|---|---|---|
| BASE     | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 |
| BASE+1   |11 |10 |12 |13 |15 |   |   |   |
| BASE+2   |   |   |   |   |17 |16 |14 | 1 |
※ アドレス範囲が0x0378-0x037Fの場合、0x0378 に0xFF を出力すると、ピンの2-9が0V出力になり、0x00を出力すると約3.5V出力になります。(ボードによっては5Vのこともある)

それぞれのピンの意味。

| ピン  |名称|信号方向|詳細|
|:---|:---|:---:|:---|
| 1 | 同期信号  | ->| この信号がLOWの状態で受信側がデータを読み取る。HIGTの状態でデータの書き換えを行う。  |
| 2 | Data0  |<->| データ  |
| 3 | Data1  |<->| データ  |
| 4 | Data2  |<->| データ  |
| 5 | Data3  |<->| データ  |
| 6 | Data4  |<->| データ  |
| 7 | Data5  |<->| データ  |
| 8 | Data6  |<->| データ  |
| 9 | Data7  |<->| データ  |
| 10| PeriphCLK  | <- |   |
| 11| PeriphACK  | <- |   |
| 12| nAck応答  | <- |   |
| 13| X-フラグ  | <- |   |
| 14| ホストACK  | -> |   |
| 15| Periph要求  | <- |   |
| 16| N受信要求  | -> |   |
| 17| 1284モード  | -> |   |
| 18| Gnd  | - | 接地線  |
| 19| Gnd  | - | 接地線  |
| 20| Gnd  | - | 接地線  |
| 21| Gnd  | - | 接地線  |
| 22| Gnd  | - | 接地線  |
| 23| Gnd  | - | 接地線  |
| 24| Gnd  | - | 接地線  |
| 25| Gnd  | - | 接地線  |

※確認したところによると、Gndは全点相互に短絡されていました。
　これが標準的なものなのか、ワタシがテストした環境独自のものかは不明です。

# テストプログラム
LinuxとWindowsそれぞれの環境でパラレルポートに対して任意の値を出力するためのプログラムを作成しました。
参考のためにここに掲載します。

```paratest.c
#define __WIN32__
#include <stdio.h>

#ifdef __WIN32__
	#include <windows.h>
	#include<conio.h>	// _outp関数

	#define sleep(n) Sleep(n * 1000)			// Sleep関数は引数がミリ秒単位
	#define outb(num, port) _outp(port, num)	// outb関数 と _outp関数では引数の並びが逆
#else
	#include <linux/types.h>
	#include <sys/io.h>
	#define HANDLE int
#endif 

unsigned short paraport = 0x0378;	//パラレルポートのアドレス
int sleeptime = 5;					// 出力一時停止時間

// パラレルポート初期化処理
HANDLE InitPara()
{
	HANDLE hndl;
#ifdef __WIN32__
	// GIVEIO.sysの設定
	hndl = CreateFileA(
		(LPCSTR)"\\\\.\\GIVEIO",
		GENERIC_READ,
		0,
		NULL,
		OPEN_EXISTING,
		FILE_ATTRIBUTE_NORMAL,
		NULL);
	if(hndl == INVALID_HANDLE_VALUE)
	{
		MessageBox(NULL, TEXT("GIVEIO Init Error"), TEXT("check for GIVEIO.sys"), MB_ICONERROR);
	}

	outb(0x00, paraport + 2);		// 制御ポートを出力モードに設定
#else
	hndl = ioperm(paraport, 8, 1);	// paraport から 8bit を出力モードに設定
	if(hndl != 0)
	{
		printf("ioperm init errer\n");
		return 0;
	}
#endif
	return hndl;
}

int main()
{
	HANDLE hndl = InitPara();	// パラレルポートへの出力制御開始

	outb(0xFF, paraport);		// パラレルポートのデータビットすべてに１を出力
	sleep(sleeptime);			// 5秒待機
	outb(0x00, paraport);		// パラレルポートのデータビットすべてに０を出力

#ifdef __WIN32__
	if(hndl != NULL){ CloseHandle(hndl); }	//GIVEIOを終了する。
#endif
	return 0;
}
```

## Linux
Linuxでは１行目をコメントアウトしてからコンパイルして下さい。
コンパイルは 

```
#gcc -o <name> <srcname.c>
```
でOKです。

Linuxではこのプログラムをコンパイルしたものをrootユーザーで実行する必要があります。
一般ユーザーで実行した場合、ハードウェアの初期化が行えずに ioperm の実行でエラー出力となります。

## Windows
私はこのソースをVisualStudio(VisualC++)でコンパイルしました。

WindowsXPでは通常のユーザーによるハードウェアへの直接アクセスは認められていないようです。
そこで、これを回避するために「giveio.sys」というドライバを使用する必要があります。

### GIVEIO.SYS とINSTDRV.EXE
先ほど書いたことですが、Windows2000(XP含む)以降ではユーザープログラムによるハードウェアへの直接アクセスは認められていないようです。
そこで、この制限を回避するために GIVEIO.SYS というドライバソフトを使用します。リンクを張りたいところなのですが、ちょっと権利的なところが不明ですので止めておきます。ググってみて下さい:sweat_smile:

さらに、 GIVEIO.SYS のシステムへの配置は結構面倒という事でこれをサポートするソフトが INSTDRV.EXE です。インストールとアンインストールが簡単に実行できます。こちらも併せてググってみて下さい。

で、INSTDRV.EXE を使用して GIVEIO.SYS のインストールが完了したら、先ほどのテストプログラムを実行できます。

### 出力確認
今回私はチョー簡易的な方法として、HIOKIの3244-60を使用してピンごとに電圧を測定しました(^^ゞ

---
*-- ここから少し違った話になってきます --*
---

# 拡張カードを 0x0378 のアドレスで使う
さて、上記のテストですがパラレルポートを使用する特殊なソフトを組む以外では使いどころがなさそうなものです。
しかし今回ワタシには特殊な目的がありました。
WindowsXP上で使用していたソフトがドングルに対して0x0378〜のアドレスでインターフェースしている（と推測される）かつ、オンボードにパラレルポートを載せたマザーボードが入手できない。
（全くできないわけではありませんが、手元になかったので）
と、言うわけでこれを回避する方法としてOSの仮想化を検討しました。

# VMWarePlayer
情報収集しつつ、いくつかのVMを実際に試した結果として VMWarePlayer に行き着きました。
VMWarePlayerでは、パラレルポートの仮想化にも対応しています。

1.もし使用するPCのオンボードにパラレルポートが装着されていれば、BIOS(UEFI)でDISABLEにしてください。
2.PCにパラレルポートの拡張カードをインストール。
3.PCにCentOSをセットアップ。（CentOS以外のディストリビューションでも、もちろんOKです）
4.CentOSにVMWarePlayerをセットアップ。
　（今回使用したのはちょっと古くて、バージョン１２辺り）
5.VMWarePlayerに仮想環境としてのWindowsXPをセットアップ。
6.仮想環境の設定にて、パラレルポートを追加。ホストの paraport0 を選択してください。
7.仮想環境上のWindowsXPから上記のテストプログラムを実装（GIVEIO.SYS とINSTDRV.EXE の環境は整えて下さい）
パラレルポートのデータビットの出力が変化することが確認できました。

## アドレス範囲の確認
仮想環境のWindowsXPでパラレルポートのIOの範囲が0x378-0x37Fになっています。
そしてホストOS(CentOS)上で ```#cat /proc/ioports``` とコマンドを打つと```d050-d052```と確認できました。
つまり、ホストOS(CentOS)上では0xD050-0xD052であると認識されているパラレルポートに、仮想環境のWindowsXPからは0x0378-0x037Fとしてアクセス出来ることになります。

## ホストOSとクライアントOS
ちなみにホストOSがCentOS、クライアントOSもCentOSの環境でもテストを行いましたが、パラレルポートへのアクセスを確認することが出来ました。

# まとめ
上記のテストを踏まえ、Windows10のPCとVMWarePlayerのコマーシャルライセンスを複数セット購入。
仮想環境上ですが非常に高価なソフトを継続して使用することができました。

# リンク

[Wiki:パラレルポート](https://ja.wikipedia.org/wiki/%E3%83%91%E3%83%A9%E3%83%AC%E3%83%AB%E3%83%9D%E3%83%BC%E3%83%88)
[]()