目次

X88000

概要

Project X88000において開発・配布されているPC-8801エミュレータ。 ソースコードはPDSとして公開されており自由に改変できる。

Linux対応版とWindows版とがあり、Linux版は音声のサポートがない。

HAL研PCG-8800にPCGおよび音源まで含めて対応している。

高性能な反面、設定可能な項目は少なく、As Isで利用する場合は問題ないが、キーバインドを変更したい、あるいは、イメージファイルの拡張子の対応を増やしたいといったような場合には、ソースに手を入れて再構築する必要がある。

開発が止まって久しいので、Linux版はさておき、Windows版はある程度手を入れないとビルドすらままならない。

改造

ビルド可能にする(Windows版)

Windows版はDirectX7をターゲットに作成されており、Visual Studio 2019 Communityなどではコンパイルすらできない。 とはいえ、いくつかのポイントを修正すればビルド可能であり、現在でも手を入れたバージョンで遊ぶことが可能である。

よくわからないが、Debugフラグで構築したところ、Windows DefencerにTrojan.Script/Wacatac.B!mlと判定され、駆除された。 Releaseフラグで構築すると特に何も言われないので、誤検出ではないかと思うが、不安な方は使わないほうがいいだろう。

修正箇所

#if 0
    typedef HRESULT WINAPI DirectInputCreateProc(
        HINSTANCE, DWORD, IDirectInput**, IUnknown*);
    DirectInputCreateProc* pprocDirectInputCreate = (DirectInputCreateProc*)
        GetProcAddress(,_hlibDInput, "DirectInputCreateA");
    if (pprocDirectInputCreate == NULL) {
        return false;
    }
    HRESULT hResult;
    // Direct Input
    hResult = pprocDirectInputCreate(
        (HINSTANCE)GetModuleHandle(NULL),
        DIRECTINPUT_VERSION, &m_pDI, NULL);
#else
    typedef HRESULT WINAPI DirectInput8CreateProc(HINSTANCE,DWORD,REFIID,LPVOID*,LPUNKNOWN);
    DirectInput8CreateProc *pprocDirectInput8Create = (DirectInput8CreateProc*)
        GetProcAddress(m_hlibDInput, "DirectInput8Create");
    if (pprocDirectInput8Create == NULL)
    {
        return false;
    }
    HRESULT hResult = pprocDirectInput8Create((HINSTANCE)GetModuleHandleW(NULL), DIRECTINPUT8_VERSION, IID_IDrectInput8W, (LPVOID*)&m_pDI, NULL);
#endif
    if (hResult != DI_OK) {
        return false;
    }

(StdHeader.h 160行目付近の #define DIRECTINPUT_VERSION 0x0300 の下に、#define DIRECTINPUT8_VERSION 0x0800を追加しておく。)

以上を修正するとバイナリを得ることができる。

カーソルキーの機能を変える(Windows版)

X88000はエミュレータとしての完成度が高いものの、キーバインドを含め、設定可能な項目が非常に少ない。 ノートPCで遊ぶ場合など、テンキーがない環境では、カーソルキーにテンキーの2,4,6,8あるいは1,2,3,5などを割り当てて、快適にゲームを楽しみたいのだが、これができない。

なので、キー入力処理に手を入れて、カーソルキーの処理を変更する。 なお、ゲームによって、2,4,6,8または1,2,3,5で上下左右の割り当てになっているので、メニューから切り替えられるようにしておく。

カーソルキーの割り付け変更

リソース(メニュー)の作成

リソースエディターで、メニューを追加する。 IDR_MAINMENU(日本語)およびIDR_MAINMENU_E(英語)の二つがあるので、両方に追加する。 X88000カーソルキーメニュー

それぞれのIDシンボルは以下のようにする。

処理タイプフラグの追加

キー処理はX88Frameの中で行われているので、フラグを、X88Frame.cppおよびX88Frame.hに追加していく。

X88Frame.hの93行目からフラグの宣言を追加。

91:    // keyboard state table
92:    static uint8_t m_abtKeyboardState[256];
93:#if 1
94:    // cursor key mapping
95:    static uint8_t m_cursorMode;
96:#endif

X88Frame.cppの122行目からフラグのインスタンスを定義する。

118:// keyboard state table
119:
120:uint8_t CX88Frame::m_abtKeyboardState[256];
121:
122:#if 1
123:// cursor keys
124:uint8_t CX88Frame::m_cursorMode = 0;
125:#endif

メニュー処理の実装

X88Frame.cppCX88Frame::WndProc()処理内に追加(汚い)

		case IDM_VERSION:
			OnHelpMenuCommand(LOWORD(wParam));
			break;
#if 1
		case IDM_CURSORKEYS_CURSOR:
			m_cursorMode = 0;
			CheckMenuItem(GetMenu(hwnd), IDM_CURSORKEYS_CURSOR, MFS_CHECKED);
			CheckMenuItem(GetMenu(hwnd), IDM_CURSORKEYS_2468, MFS_UNCHECKED);
			CheckMenuItem(GetMenu(hwnd), IDM_CURSORKEYS_1235, MFS_UNCHECKED);
			break;
		case IDM_CURSORKEYS_2468:
			m_cursorMode = 1;
			CheckMenuItem(GetMenu(hwnd), IDM_CURSORKEYS_CURSOR, MFS_UNCHECKED);
			CheckMenuItem(GetMenu(hwnd), IDM_CURSORKEYS_2468, MFS_CHECKED);
			CheckMenuItem(GetMenu(hwnd), IDM_CURSORKEYS_1235, MFS_UNCHECKED);
			break;
		case IDM_CURSORKEYS_1235:
			m_cursorMode = 2;
			CheckMenuItem(GetMenu(hwnd), IDM_CURSORKEYS_CURSOR, MFS_UNCHECKED);
			CheckMenuItem(GetMenu(hwnd), IDM_CURSORKEYS_2468, MFS_UNCHECKED);
			CheckMenuItem(GetMenu(hwnd), IDM_CURSORKEYS_1235, MFS_CHECKED);
			break;
#endif
		}
		break;

本来は、ラジオボタンにするべきだろうが、今時のメニューはどうするべきなのかちゃんとわかってないので、汚い処理になっている。

キー処理の変更

X8Frame.cpp CX88Frame::UpdateAllKeyMatrics()処理内に以下の変更。

	} else {
		bShift = IsKeyPressed(VK_SHIFT);
	}
#if 0
	bool bUpArrow = IsKeyPressed(VK_UP),
		bRightArrow = IsKeyPressed(VK_RIGHT);
	if (X88k().PC88().Z80Main().GetBasicMode() ==
			CPC88Z80Main::BASICMODE_N80V1)
	{
		if (IsKeyPressed(VK_DOWN)) {
			bShift = bUpArrow = true;
		}
		if (IsKeyPressed(VK_LEFT)) {
			bShift = bRightArrow = true;
		}
	}
#else
	bool bUpArrow = false, bRightArrow = false;
	if (m_cursorMode == 0||IsKeyPressed(VK_CONTROL))
	{
		bUpArrow = IsKeyPressed(VK_UP);
		bRightArrow = IsKeyPressed(VK_RIGHT);
		if (X88k().PC88().Z80Main().GetBasicMode() ==
			CPC88Z80Main::BASICMODE_N80V1)
		{
			if (IsKeyPressed(VK_DOWN)) {
				bShift = bUpArrow = true;
			}
			if (IsKeyPressed(VK_LEFT)) {
				bShift = bRightArrow = true;
			}
		}
	}
	else
	{
		if (IsKeyPressed(VK_DOWN) || IsKeyPressed(VK_LEFT) || IsKeyPressed(VK_RIGHT) || IsKeyPressed(VK_UP))
		{
			if (m_cursorMode == 1)
			{
				X88k().SetKeyMatrics(0x00, 2, IsKeyPressed(VK_DOWN));
				X88k().SetKeyMatrics(0x00, 4, IsKeyPressed(VK_LEFT));
				X88k().SetKeyMatrics(0x00, 6, IsKeyPressed(VK_RIGHT));
				X88k().SetKeyMatrics(0x01, 0, IsKeyPressed(VK_UP));
			}
			else
			{
				X88k().SetKeyMatrics(0x00, 2, IsKeyPressed(VK_DOWN));
				X88k().SetKeyMatrics(0x00, 1, IsKeyPressed(VK_LEFT));
				X88k().SetKeyMatrics(0x00, 3, IsKeyPressed(VK_RIGHT));
				X88k().SetKeyMatrics(0x00, 5, IsKeyPressed(VK_UP));
			}
			return;
		}
	}
#endif
	X88k().SetKeyMatrics(0x00, 0, IsKeyPressed(VK_NUMPAD0));
	X88k().SetKeyMatrics(0x00, 1, IsKeyPressed(VK_NUMPAD1));
...
        }
        else
        {
            bShift = IsKeyPressed(DIK_LSHIFT, DIK_RSHIFT);
        }
#if 0
        bool bUpArrow = IsKeyPressed(DIK_UP),
            bRightArrow = IsKeyPressed(DIK_RIGHT);
        if (X88k().PC88().Z80Main().GetBasicMode() ==
                CPC88Z80Main::BASICMODE_N80V1)
        {
            if (IsKeyPressed(DIK_DOWN)) {
                bShift = bUpArrow = true;
            }
            if (IsKeyPressed(DIK_LEFT)) {
                bShift = bRightArrow = true;
            }
        }
#else
	bool bUpArrow = false, bRightArrow = false;
	if (m_cursorMode == 0||IsKeyPressed(DIK_LCONTROL, DIK_RCONTROL))
	{
		bUpArrow = IsKeyPressed(DIK_UP);
		bRightArrow = IsKeyPressed(DIK_RIGHT);
		if (X88k().PC88().Z80Main().GetBasicMode() ==
			CPC88Z80Main::BASICMODE_N80V1)
		{
			if (IsKeyPressed(DIK_DOWN)) {
				bShift = bUpArrow = true;
			}
			if (IsKeyPressed(DIK_LEFT)) {
				bShift = bRightArrow = true;
			}
		}
	}
	else
	{
		if (IsKeyPressed(DIK_DOWN) || IsKeyPressed(DIK_LEFT) || IsKeyPressed(DIK_RIGHT) || IsKeyPressed(DIK_UP))
		{
			if (m_cursorMode == 1)
			{
				X88k().SetKeyMatrics(0x00, 2, IsKeyPressed(DIK_DOWN));
				X88k().SetKeyMatrics(0x00, 4, IsKeyPressed(DIK_LEFT));
				X88k().SetKeyMatrics(0x00, 6, IsKeyPressed(DIK_RIGHT));
				X88k().SetKeyMatrics(0x01, 0, IsKeyPressed(DIK_UP));
			}
			else
			{
				X88k().SetKeyMatrics(0x00, 2, IsKeyPressed(DIK_DOWN));
				X88k().SetKeyMatrics(0x00, 1, IsKeyPressed(DIK_LEFT));
				X88k().SetKeyMatrics(0x00, 3, IsKeyPressed(DIK_RIGHT));
				X88k().SetKeyMatrics(0x00, 5, IsKeyPressed(DIK_UP));
			}
			return;
		}
	}
#endif
        X88k().SetKeyMatric(0x00, 0, IsKeyPressed(DIK_NUMPAD0));

#if 0で無効化しているのがもともとのカーソルキー処理。 PC-8001などは、カーソルキーが縮退1)しているので、その処理をしている。 ここで、m_cursorModeが0かコントロールキーが押されていたら、カーソルキーとして処理し、そうでなければ、テンキーの振りをするだけである。

1)
上下と左右がそれぞれ一つのキーにまとめられ、シフトキーの有無で向きを変えていた。