====== X88000 ======
===== 概要 =====
[[https://quagma.sakura.ne.jp/manuke/x88000.html|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フラグで構築すると特に何も言われないので、誤検出ではないかと思うが、不安な方は使わないほうがいいだろう。
=== 修正箇所 ===
* StdHeader.h:112~117の一連の''typedef''を無効化する。
* コンパイルエラー(''LPDWORD''に変換できない)を起こす''uint32_t''宣言の変数をすべて''DWORD''宣言に変更する。
* ライブラリ''dinput.lib''および''dinput.dll''はそれぞれ''dinput8.lib''および''dinput8.dll''に置き換える。
* DirectInput8 のインスタンスが作成されるようにする。(''X88irectX.cpp''内376行目付近から -- 下記)
#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で上下左右の割り当てになっているので、メニューから切り替えられるようにしておく。
{{::x88000:x88cursorkey.png?600|カーソルキーの割り付け変更}}
=== リソース(メニュー)の作成 ===
リソースエディターで、メニューを追加する。
''IDR_MAINMENU''(日本語)および''IDR_MAINMENU_E''(英語)の二つがあるので、両方に追加する。
{{::x88000:x88menu.png?600|X88000カーソルキーメニュー}}
それぞれのIDシンボルは以下のようにする。
* IDM_CURSORKEYS_CURSOR
* IDM_CURSORKEYS_2468
* IDM_CURSORKEYS_1235
=== 処理タイプフラグの追加 ===
キー処理は''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.cpp''内''CX88Frame::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などは、カーソルキーが縮退((上下と左右がそれぞれ一つのキーにまとめられ、シフトキーの有無で向きを変えていた。))しているので、その処理をしている。
ここで、''m_cursorMode''が0かコントロールキーが押されていたら、カーソルキーとして処理し、そうでなければ、テンキーの振りをするだけである。