突発企画 CP/M-80 エミュレータ開発日記 2004/3/5 ふと唐突に、CP/M-80エミュレータを開発しようというという気分になる。 きっかけは、ネット上で配布されていたCP/M版ZORK。 EmuZ-2500でP-CP/Mが動いているからこれで実行してみようと思ったものの、 配布がディスクイメージではなくCP/M上のファイルであったため、そのままでは 実行できない。 でもCP/M用ディスクイメージの操作ツールを作成するのもいまいち面倒くさい。 DOS汎用だったら他にも使い道があるんだろうけど。 で色々調べてみたところ、CP/Mエミュレータというのは幾つかあるようで、 ディスクイメージではなくCP/M上のファイルをそのまま操作できるものも 結構あるらしい。 DOS汎用のものがあったので、Win2000上のコンソールで実行してみたところ、 確かにZORKが起動するのを確認できた。 とは言え、win32ネイティブなCP/Mエミュレータはどうもないらしい。 それにどうせやるならsigmarion3上でZORKをプレイしてみたいというもの。 という訳で、今更といえば余りに今更なネタではありますが、win32用の CP/Mエミュレータを開発してみることにしました。 仕事が忙しくてEmuZ-5500が止まってしまっていたので、開発再開前のリハビリ としては丁度よい規模かな、と思います。 2004/3/6 我が姉上の華燭の典により上京、非常にめでたい。 義兄は中学時代にPC-6601や8801mk2SRを弄られていたそうで、当時はまってた ゲームの話題でしばし盛り上がる。レベル8で鍵はまり、イロイッカイズツ。 ついでに秋葉によって、CP/Mの資料がないか探してみたところ、ザコンの6階に 87年に出たCP/MとMS-DOSの解説本の初版第1刷!がまだ店頭にならんでました。 いやはや、探せばあるもんだ。 開発方針。 ターゲットは取り合えずCP/M-80 V2.2とする 将来はP-CP/M (V2.8?)やCP/M Plus(V3.0)への拡張も目指して、できるところで 拡張BDOS/BIOS部も実装していく。 仮想BDOS/BIOSをwin32側に実装して、各々のエントリ部でそれらをコールする (擬似I/Oを使うか、未定義命令を使うか)ことで、仮想的にCP/M互換環境を提供。 ccpなしであれば、実際のCP/Mのリソースを使用しなくても大丈夫かと。 単にCOMファイルを0x100からロードして、ゼロページに引数を入れてやって、 あとは0x100にジャンプすればいける、筈。多分。 ROMなしでBDOS/BIOSもエントリ部のみのダミーなので、TPAはできる限り大きめに 取れるようにする。 起動時には、コモンダイアログで*.COMを指定して、すると先頭に該ファイル名の 入ったインプットボックスが表示される。必要であればそれにオプションを 付けて、コマンドラインを指定すると、仮想マシンが駆動し始める。 BDOSには、1行入力などすぐには処理が返らないタイプのものが幾つかあるため、 win32側で実装するとなると、その間は仮想マシンのCPUが止まってしまう。 そのため、EmuZ-2500でやっているように、一定クロック数だけCPUを回して、 画面を更新して、時間経過を待つというタイプの実装ができない。 よって、マルチスレッドにして、CPUおよびBDOS/BIOSの駆動は別スレッドで 行うような実装にする。 CPUコアは、EmuZ-2500で使用しているものを流用。 但し、前段の理由により、駆動方法は1オペレーションごと実行すること。 クロック調整はせず、適当なオペレーション数ごとにSleep(1)を入れるだけで、 基本的にはスピード全開で動作させる。 ファイルはディスクイメージではなく、windows上のファイルをそのまま扱う。 起動時にCOMファイルを指定すると、そのフォルダ丸ごとがドライブ1であると 認識するようにする。 こうなると、ファイル操作関連はBDOS上のみで、イメージで扱う必要のある BIOS上のシステムコールは全部サポート外ということになる。 コンソールは独自実装。 AllocConsole()のコンソール画面は、WinNT系列ではANSIエスケープシーケンスを サポートしない(configを弄ればいいらしいけど、環境依存はしたくない)のと、 sigmarion3への移植を念頭においているので、残念ながら使用できない。 まずはANSI互換で作成して、将来はVT-52互換モードもサポートするようにする。 拡張BDOSのメモリブロック転送で定義されている、CP/MのVRAM構造をそのまま サポートできるようなものにする。 また一応半角カナとShift-JIS漢字も受け付けられるようにする。 コンソール入力は、WM_CHARで飛んできたものをそのまま渡す。 漢字やCTRL+(アルファベット)についてもそのまま取得できるらしい? 処理的には、hwndだけ事前に渡しておいて、仮想VRAMが更新されたら自分で勝手に WM_PAINTを発行させるようにする。 またスクリーンサイズやフォントは自由にカスタマイズできるようにする。 開発目標は、 第1段階:Zorkが完全動作する。 第2段階:sigmarion3上にソース共通で移植する。 第3段階:エディタやインタプリタ、コンパイラなど開発環境が動作する。 -------- ここまでこれば実用性はあり -------- 第4段階:擬似CCPを実装して、完全なCP/M環境を実現する 第5段階:P-CP/MやCP/M PlusのBIOS/BDOSも実装する。 -------- ここからやや蛇足 -------- 第6段階:擬似VCCP対応。 -------- 需要あるのか? -------- 第7段階:CP/M-86やCP/M-68kも実装する。 第1段階まで1週間で開発することを目指す。 また第2段階まで2週間で開発することを目指す。 第3段階までは、継続して開発を行う。 第4段階は、XCCPをデフォルトで起動するようにしておけばいいのかな? 第5段階は、P-CP/MやCP/M Plusを必要とするキラーアプリがあれば。 第6段階以降は、今後EmuZ-5500が行き詰まったときの気分転換に。 2004/3/8-9 コンソールクラス実装開始。 当面はCP/Mエミュレータのみ使用の予定だけど、将来は汎用的に使用できるものに したいところ。 処理の基本としては、1文字受け取ったら仮想VRAMに格納して、カーソルを進める。 行末だったら次の行に進み、それが最終行を超えれば1行スクロール。 仮想画面で変更のあった部分については、DIBSectionを書き換えて、InvalidRgn()と UpdateWindow()を実行して、ウィンドウの更新を行う。 後は、制御文字やエスケープシーケンスに応じて色々と処理を行えばよい。 取り敢えず本日は、1文字を受け取って仮想VRAMを操作するところまで。 エスケープシーケンスについては、ESC[PL;PcH, ESC[PL;Pcf, ESC[PnA, ESC[PnB, ESC[PnC, ESC[PnD, ESC[s, ESC[u, ESC[2J, ESC[K, ESC[Ps;...;Psm については 実装済み。 制御文字は、BEL, BS, HT, LF/VT/FF(全てLF扱い), CR, DELに対応。 DELは一応カーソル位置の文字を削除としているが、実際には無視した方がよい? BELは、Beep()はOSによって動作が異なるのでPlaySound()で鳴らす予定。 2004/3/9-10 仮想VRAMが変更されたときに、それをウィンドウに反映する処理を実装。 また任意のフォントをChooseFont()で指定したとき、フォントの最大幅、高さを 取得して、それにあわせてウィンドウのサイズを変更する処理を実装。 これで、出力に関してはコンソールとして十分に機能する筈。 試しにcpmクラスでスレッドを起動して、そこの中でコンソールにテキストを 吐き出しつづける処理を実行したところ、想定通りの動作をすることを確認。 CPUの駆動や、BDOSですぐに戻らない処理(1行入力など)は、このスレッドで 駆動することになる。 2004/3/10-11 コンソールで漢字を表示できるようにしてみる。 まずフォントのキャラクターセットがSJISであるかで漢字対応/非対応を判定。 後は1文字表示が来たときに、0x81〜0x9f, 0xe0〜であった場合はもう1文字 来てから纏めて仮想VRAMに入れると共に、アトリビュートにも漢字の1バイト目、 2バイト目属性を付けてやるだけ。 表示側は、1バイト目属性のときは次の1バイトと合わせて漢字として表示、 2バイト目属性のときは処理をパスする。 1バイト目と2バイト目が行末で別れてしまった場合は、前の行に左半分だけ 表示されることになる。 コンソール側に、1文字/行入力取得関数を実装する。 これらは入力が終わるまで戻らない類の関数のため、別スレッドで駆動する。 手始めに、WM_CHARで受け取った文字をバッファに入れる関数、バッファを空にする 関数、バッファにデータがあるか判定する関数、バッファを空にする関数、を実装。 バッファは16文字まで受け付けることとする。 1文字入力は、スレッド起動して、そちらでキーバッファの状態を見て、入力が あればそれを取得してスレッド終了、スレッド終了を判定してリターン。 1行入力は、スレッド起動して、そちらでキーバッファの状態をみて、入力に応じて エコーバックしたりバックスペースしたりして、リターンがきたらスレッド終了、 スレッド終了を判定してリターン。 各々、入力が完了する前にプログラムが終了される場合があるので、外部から スレッドを終了できるようにする。 終了の順番としては、コンソールのスレッドは仮想マシンのスレッドから起動され、 また仮想マシンはコンソールのポインタを持っているので、 1. コンソールのスレッドを停止 →このスレッドを起動した仮想マシンのスレッドに戻る 2. 仮想マシンのスレッドを終了 3. 仮想マシンを破棄 4. コンソールを破棄 とすれば、取り敢えず問題なく終了するっぽい。 まだコントロールキーの編集機能が全然実装されてないけど、BIOS/BDOSを実装する 部品は揃ったかな? メニューを追加。雛型ということで、項目はこれだけ。 File Open Exit Config Font Exitは、単にPostQuitMessage(0)をするだけ。 Openは、GetOpenFileName()でファイル名を取得、そのパスをcpm->Run()に 渡して仮想マシンを起動する。 仮想マシン側では、フォルダのパスとプログラム名に分解して、起動画面を 表示&コマンドの引数入力状態に入る。 Fontは、ChooseFont()でフォントを取得して、コンソールを再初期化すると 共に、メインウィンドウのサイズも変更する。 フォント選択(+スクリーンの文字数)の設定は、WinCE環境ではiniファイルが 使用できないことを考慮して、fread, fwriteで保存する。 2004/3/12-14 console, cpmの各スレッド内で扱う変数をpublicで宣言しているのが気に入らない。 そこで、クラスのポインタおよびスレッド内で扱う変数を含む構造体を定義して、 それを受け渡しするようにする。 機能的に変わるという訳ではないけど、ちょっとすっきり。 CPUからCP/M仮想マシンのBIOS/BDOSのコールの方法については、Z80の処理が定義 されていないオペコードの一部をダミーとして使用することにする。 但し一応誤爆も考慮して、PCの範囲もチェックする。 BIOSコールはED xx系列の命令が定義されていない領域を使用する。 BOOTから順にED E0, ED E1, ・・・と割当。 BDOSコールはED FFとする。 CP/M仮想マシンの初期化処理。 まずはゼロページの初期化。 入力されたコマンド列を分解して、FCBに格納する処理。 先頭から引数を取得して、ドライブレター、ファイル名、拡張子に分解。 *の?への展開もしつつ、メモリに格納していく。 BODS, BIOSの領域は、上で決定したダミー命令とRETをひたすら羅列するだけ。 BDOS, BIOSの実体となる関数を追加。 まだ中身からっぽで、呼ばれた機能番号と引数のレジスタの値をログ出力するだけ。 ここまで実装したところでZORK1を実行してみる。 見てみると、ひたすらファイルオープンしてランダム読込してファイルクローズ して、を繰り返してるっぽい。 実績あるから当然だけど、CPUは正常に動作していると見ていいかな。 ディスクアクセス周り実装前に、情報を整理。 1レコードは128bytes。 1ブロックは128レコードで16kbytes。 シーケンシャルアクセスでは、ブロック番号+レコード番号で位置を指定。 ランダムアクセスでは、レコード番号で位置を指定。 で、ディスク操作周りの実装。 同じファイルを何度でも開けたり、違うFCBアドレスで同じアドレスを操作したりと 色々面倒くさい。 取り敢えず、毎回FCBからファイル名を取得、データベースからそのファイル名で 既に開いているファイルが無いかを判定するようにする。 また開くときはreadかwriteかも判らないので、取り敢えずreadで開いておいて、 後で違う操作モードで操作するときは、その都度開き直すことにする。 ZORKで使用しているものだけ優先的に実装していく。 無事、ZORK起動。 目標どおり1週間で到達、めでたい。 ここで一度公開を行う。 2004/3/14-15 今朝の公開に対する反応。 navaroneさん@e9は変わらず律儀なお方だなあと感嘆してみたり。 こちらは長らく不義理してしまってて本当に済みませんです。 取り敢えず、なばさん以外の唯一の反応に従って適当に改名してみますか。 #えと、別に全然怒ってないっすよ>309氏 #書き方悪かったです。 任天堂、GBA系エミュレータの特許取得の件@retropc.net。 いいかげん賞味期限の切れてるuonesterは畳んだ方がいいのかなあと思案中。 retropc関係は、なるたけメーカと連携して、メーカの利益にもなる方向で動いて いきたいとは思っているんですが。 なんか時々タスクが残ってしまうみたいなので対策。 コンソールのスレッド終了後、CPUのスレッドが終了する間に新たにコンソールの スレッドを起動してしまって、それが残っているのではないかと。 で、終了処理開始後は新たなスレッドを起動できないようにしてみる。 ファイルを選択してコマンドライン入力中に、新しいファイルを開こうとした 場合にフリーズする問題の対策も行う。 エスケープシーケンスその他の修正。 MULTIPLANの画面の乱れを見ると、=xyではなく、=yxなのかな? ^Zで全画面消去というのもあるらしいが、カーソルは移動しない? 取り敢えず、MULTIPLANの都合に合わせて実装しておく。 MBASIC.COMはなんだか謎のBDOSコールをしているらしい。 66,71,72って独自実装か何かか? 何だか良く判らないけど、当面は保留とする。 ファイル操作関係。 エラー時のHの返り値が正しくセットできてなかったのを修正。 なんでまた、こんなミスをしてたんだか。 いい加減、寝ぼけてたのかしら。 ファイルサーチ関係。 最初のファイルサーチ時に全てのファイルを検索しておいて、次のファイル以降に ついては、その結果をコピーするだけ。 ファイル名中の?の判定はファイルサーチ以外では全くしていないんだけど、 ファイルサーチ以外でも実装する必要があるのだろうか。 またランダム書込みで飛び地に書き込もうとした場合はどうしたものか。 必要なだけゼロ書込みすればいいんだろうか・・・取り敢えずは保留。 ファイルのリネーム。 リネームのAPIって何だろうと調べてみたら、MoveFile()を使うのね。盲点。 ファイルの削除は単純にDeleteFile()で。 更新後にファイルのクローズ処理にオオボケ発見。 修正修正、と。 2004/3/15 全然関係ないけど、電源の切れてる筈のsigmarion3が突然起動して、怒首領蜂の mp3を再生し出すのは、心臓に悪いので勘弁してください。 そんなことされると、先日業務用モニタごと購入した、ぐわんげと怒首領蜂大往生の 基板に呼ばれてしまうのです(を ファイルオブジェクトの初期化と開放のロジックが変だったので、コンストラクタで 初期化、デストラクタで開放、CP/M駆動スレッドの先頭と末尾で全ファイルの クローズ、と単純化する。 何故か実装し忘れていたBDOS func 0を追加。 ついでに、システムコールでCP/M駆動スレッドを停止するときに、スレッドから 呼ばれた関数内でスレッドが終了するまで待機と、ロジックが変なことになって いたのを修正する。 これで、タスクが幽霊化するのが修正できているかと。 RETでプログラムを終了してCPPに戻ろうとするプログラムの対策。 スタックにCPPのベースアドレスを入れておいて、CPPの先頭にBIOS func BOOT()を 実行するコードを入れておく。 ファイルのシーケンシャル読み書きにおいて、cr, exの更新処理が間違っていたのを 修正する。なんか違うアドレス書き換えてた(汗)のと、レコード番号が128を 越えてしまうようになっていた。 ファイルのランダム書込み。 飛び地のレコードに書き込むときは、間の空白のレコードを0で書き込むようにする。 あまり正しい処理とは思えないけど、まあ仕方ないか。 ゼロ書込みを伴うランダム書込みも一緒の実装で。 ついでに、ドライブ周りの拡張を。 今までドライブAのみサポートとしていたのを、A-Pまでフルサポートするように。 但し、B-Pについてはパスは指定できず、exeのあるドライブにフォルダを作成して それをマウントするようにする。 いや、単にパスを設定するUIF作るのが面倒くさいというだけなんだけど。 ファイルサイズの取得は、CreateFile()して、GetFileSize()して、CloseHandle()。 あー、全然Win32と分離できてない(苦笑) BDOS func 32のユーザIDの設定と取得。 一応設定した値を取得できるようにはしておく。・・・けど意味あるのかな。 HITECH CでBDOS func 102というのが来ているので調べてみると、どうもCP/M 3.0の システムコールらしい。 少しずつ、CP/M 2.8/3.0系のシステムコールも実装していかなあかんのかな。 取り敢えず、$$EXEC.$$$にコマンドらしいデータ吐き出して、そこで止まる。 まだまだ掛かりそう。 色々調べたところ、何か変なレコードを順読み出ししようとして死んでるくさげ。 で、一応ファイルオープン/新規ファイル作成時に、FCB内のex, s1, s2, cr, r0, r1, r2(要はレコード位置を指定するパラメータ)をゼロクリアしてやると、 なにげにコンパイルを開始するように。 s2以外は本来CP/Mのソフト側がやるべきだと思うんだけどなあ(苦笑) ちゃんとコンパイルできるソースコードがないから最後までいくかは判らないけど、 まあ一歩前進ということで。 2004/3/16-17 exの解釈を間違えていたのを修正。 exの値域は0x00-0x1fらしい。 ファイル操作時のcr, ex, s1, s2の更新は、ファイル操作が成功した場合のみ 行うように変更する。 ファイルのクローズし忘れなどが無いように、操作するその度に一々FCBから ファイル名を取得して、ファイルのオープン、クローズをするように。 順読み出し時にレコードが1つずれていた問題を修正。 以上、ファイル操作周りの実装を修正したところ、HITECH-CでCのソースコードを コンパイルできること、また出来上がったプログラムが実行できることを確認。 目先の目標は達成できましたので、明日からはP-CP/M, CP/M 3.0のBDOSの実装 およびWinCE環境への移植作業に入ります。 作業量少ないですけど、眠いので今日はこれまで。 2004/3/18 CATCHUMがBDOS func 31(ディスクパラメータ取り出し)を使っている模様。 対応しなきゃ駄目かー。 Ladderは正常に動いているっぽいけど、速過ぎてゲームにならない(苦笑) 今はZ-80コアを全力で回してるからなあ。 各BDOSファンクションの実装見直し。 本格的に、P-CP/M, CP/M Plusの仕様を取り入れていく。 #6 (direct console I/O) : E=0xFDの場合、入力するまで待機を実装。(CP/M Plus) #9 (output string), #110 (get/set string delimiter) : デリミタの変更を実装。(P-CP/M, CP/M 3.0) #10 (buffered console input) : DEが0の場合はDMAアドレスを使用する処理を実装。(CP/M Plus) #12 (return version number) : CP/M 2.2では0x22, P-CP/Mでは0x28, CP/M Plusでは0x31を返す。 そのうち設定で変更できるようにしよう。 #13 (reset discs) : CP/M 2.0系列では、Aドライブにログインして、$で始まるファイルがあれば A=0xff, なければA=0x00とするらしい。 #15 (open file) : ex, s1で表されるブロックのアロケーションマップを、ファイルサイズから 擬似的に作成するように。 またディレクトリ情報を、ゼロページの0x80-0x9fにコピーするようにした。 ex, s1, r1, r2, r3の初期化はしなくても動作する事を確認。 多分シーケンシャルリードでレコードがずれていたのが悪さしてたのだろう。 #17 (search for first) : ファイル検索ルーチン内で、FindClose()のし忘れがあったのを修正。 やべーやべー。 Last Record Byte Count関係。 CP/M Plusでは、#15, #17, #18, #30において、Last Record Byte Countを格納する 機能があるらしい。誤爆が怖いけど、#15, #17, #18に実装しておく。 ソフトの更新はないけど、日記だけアップしておくべし。 2004/3/19-20 メニュー構成の変更。 主に使い勝手の向上を図るため。 File Open - Exit - Recent #1 Recent #2 Recent #3 Recent #4 Recent #5 Terminal Font - 80x48 80x24 40x24 Version CP/M 2.2 P-CP/M CP/M Plus 最近使ったファイルは過去5件まで履歴に残る。 画面サイズは取り敢えず80x48, 80x24, 40x24のみに限定。 但し、大概のソフトは暗黙に80x24を期待しているようなので、それ以外の スクリーンサイズではいまいち使いにくいかもしれない。 まあZORK程度であれば、大量のメッセージが表示されたときのMOREの処理を 除けば、特に問題なく使えるかと。 なお40x24は将来のPocketPCに対する備え。 スクリーンが240x320なので、5x10のフォントで縦横のマージンを1ドットの場合、 240x264に収まる計算になる。 画面下には標準のIMが表示されるだろうから、サイズ的には丁度よいと言える。 バージョンは、現時点ではBDOS func 12の値に影響するだけ。 処理内容を各バージョン相応にするかは今後要検討とする。 consoleクラス内にて、描画範囲の補正処理に間抜けなミスがあったのを修正。 各BDOSファンクションの実装見直しの続き。 #20 (read next record), #21 (write next record) : CP/M Plusでは、(128 * n)単位で読み書きが可能らしいが、指定の仕方が不明。 #22 (create file) : 既に指定のファイルが存在する場合、従来はそのまま新しいファイルで上書き していたが、本来はエラーとなる。 #24 (return bitmap of logged-in drives) : 取り敢えず、A-Pまでログイン済み扱いで0xffを返している。 #28 (software write-protect current disc), #29 (return bitmap of read-only drives), #37 (selective disc reset) : 新規対応。 再起動するか、#13, #37が実行されるまでRO属性は解除されないものとする。 RO属性は、リネームにも影響するのかな? #30 (set file attributes) : ファイル属性の変更は未実装。 但し、ファイルの存在の判定とLast Record Byte Countの格納は行う。 2004/3/21 BIOS #4、間違えてEレジスタを出力してました。正解はCレジスタ。おおぼけ。 で、修正したところ、ROGUEやWSが動作し始める。 というか、元々動いていたんだろうけど画面がクラッシュしていただけと。 ただ、まだ画面がおかしいので、コンソールへの出力のログを取ってみると、 まあ出てくる出てくる、未対応のエスケープシーケンス。 という訳で、ESC ), ESC (, ESC >, ESC <, ESC *, ESC 0-9 に対応。 但し、ESC >, ESC <についてはハイライト未対応ということで、リバース扱い。 これで目に付くものは大概対応できたとは思うんだけど、どうだろ。 取り敢えず、目先の目標だったWordStarが動いたので、明日はWindowsCE環境への 対応を行う予定。 ただ新マシンへの環境移行作業も予定しているので、明日中は難しいかも。 WinCE環境への移植開始。 といっても、_T()の付け忘れがあったのを幾つか直したのと、使えないAPIを 使用していた個所を置き換えた程度。 sigmarion3ではあっさり起動するも、PocketPCでは謎の一般保護エラー。 PocketPC版については尚調査が必要。 要望にお応えして、グリーンモニタ仕様をでっちあげる。 MZ-80B/2000チックで素晴らしいので、次回あたり正式仕様に格上げします。 2004/3/22-24 グリーンモニタ正式対応。 UIFに設定追加して、カラーと切り替えられるようにしただけ。 また、画面右下に文字を出力してスクロールした場合に、該当する文字が描画 されていなかったのを修正。 あとは、CPUの負荷が大きくなりすぎない程度に高速化してみる。 これによって、sigmarion3上でもそこそこの速度で動作するように。 Pocket PC版。 処理を追いかけてみたところ、CreateDIBSection周りで落ちていたので、 与えるパラメータを決めうちにして実行するように変更することで、一応は 動作するように。 しかし、なんかPocketPCって訳判らないところで落ちることが多い気がする。 API内部のエラートラップが甘くて、PC上ならエラーコード出して返ってくる ところが、そのまま落ちてしまっているような。 2004/3/28 ロードマップ、というか今後の作業内容(予定) 1. CP/M 2.2で未実装のBDOSコール#27と#31の対応。 2. PocketPC版のフォントを何とかする。 3. 実装済みのBDOS/BIOSコールで、挙動が実物と異なる部分の修正。 4. P-CP/M, CP/M-Plusで未実装のBDOSコールのうち、必要そうなものの対応。 2004/4/2 まずPocketPCのフォントが小さすぎて視認性が悪い問題から。 これは、自分で8x5のフォントデータを作成することで対処する。 ついでに、SIPを残してフルスクリーンにするようにしたところ、コンソール部の 240x216とSIPを含めて、240x320の画面にほぼぴったり収まるように。