MC68000エミュレータの開発 その5

細々と開発を続けているMC68000エミュレータ

そろそろちょっとしたバグでも大きな影響が出始めてる…。

中には「え?!なんでこれで今まで動いてたの??」と思うような実装抜けも見つかって驚愕する(^^;

 

特に驚いたのがmove命令でのフラグ操作。コメントには巧妙にも「あとで実装する」と書かれており、すべてのフラグ操作が実装されていなかった…汗

なぜこれで動いてたのか…(-_-;

f:id:PocketGriffon:20211223105435j:plain

フラグ変化を修正して、ようやく57万ステップまで実行が進んだ。

テレビコントロールは必要なかろう…と思い、IOCS自体をスキップさせてしまった(先頭でRTSしてる)。

エラーで止まってるのはBSET命令の一部アドレッシングがまだ未実装ですよという意味だ。起動してから0.589秒相当まで実行できた。まだまだ先は長い…(T-T)

はたしてまともに動くようになるまでモチベーションは保てるのだろうか…汗

 

命令を実行してみる…準備!

さあ!ブログの方でもついに命令をエミュレーションしてみる!(^-^)

気合入れていくぜー!←カラ元気

 

ブログを書いてみてよく分かったが、言葉の説明がしきれない。

例えばアセンブラを使ったことのある人であればお馴染みの言葉である「アドレッシング」や「イミディエイト」などの説明はこのブログでは出来ない。申し訳ないが、意味が通じない方はそれっぽく読む検索などをしてもらいたい(^^;;

 

…いざ命令を実行しようとすると、その前にたくさんの準備が必要な事に気がつく。

まず、エミュレータとは言え、何かしらの動くプログラムが欲しい。そうでないと闇雲に命令を実行する事になってしまい、最初っからモチベーションが保てない(^-^;

 

ここは最終目的でもある、X68000のプログラムを実行させる。

手元にCompact XVIがあるので、そこからROMを抜き出す事にした。

実を言えば、私自身がX68000のメモリマップをちゃんと把握していないw

いつのもマイナーマシンであれば、まずは全メモリをホストマシンへ送り込んで解析を…とするところだけど、X68000は人気マシンということもあるので豊富な書籍を参考にする(^^)

f:id:PocketGriffon:20211223125620j:plain

どうやら……いわゆるROMプログラムは$FE0000からの128KB、そしてフォントデータが768KBあるらしい。フォントでかっ!(@_@;

 

このデータをどうやってホストマシンへ送り込もうと思ったが…先日、手元のマシンにRaSCSを繋げた。X68000本体でファイルに変換する事が出来れば、あとはネットワーク経由でホストマシンへ送り込める。実に簡単だ(^-^)

 

f:id:PocketGriffon:20211223130019j:plain

ここも数あるツールの力を借りることにした。

SCD(デバッガ)でメモリを書き出すだけでファイルにすることが出来た!

この2つのファイルをエミュレータ用のプログラムで読み込むようにしよう。

 

次にエミュレータ用のメモリを用意する。

ここは作りを単純化するために、68000で使えるメモリの最大容量(16MB)を用意する。

今の時代だったら16MBを確保する事にそんな罪悪感は沸かない(^^)

1990年頃、初めてワークステーションを利用した際に128KBのメモリをallocするコードを書いた事があったけど、その時の申し訳なさ感ときたらスゴイものがあった(^^;

far付けなくても128KB確保出来るのにも驚いたけど…汗

 

確保したメモリの$F00000からフォントROM、$FE0000からプログラムROMを読み込んでおき、X68000実機と同じメモリマップにしておく。他にVRAMやI/Oの領域があるが、今のところは放置で構わない(メモリは存在している)。

 

ここではて?と思った。

MC68000は$000000に書かれている8バイトをSSPとPCにコピーをして起動する。でも…メモリマップ的に$000000には何もない…。あれ?あれれ??

X68000ってどういう起動プロセスになってるんだ???(@_@;;

 

ここから……だいぶいろんな本やら資料をひっくり返してみたが、どうにも詳細を書いてる情報がない!ぐぬぬ、これは困ったぞ…(T-T)

  :

  :

そして答えはひょんなところから見つかった。

しかもついさっきまで見てた本から(^^;;

f:id:PocketGriffon:20211223132406j:plain

リセット直後のみ$FF0000〜と同じ内容が読める

ぐはっ!なんという仕様!(^^;

しかし……「リセット直後」は良いけれども、リセット後いつまで見えてるんだろ??こういう事がエミュレータを作っていると正確に知りたくなる。うむむ…(-_-;;

 

ともあれ、MC68000の起動に必要な情報は分かった!

SSP=$2000、PC=$00FF0010にレジスタを初期化し、実行開始となる。

他のレジスタ(AnやDnなど)については未初期化…なんだと思われれる…。

 

メモリアクセス

エミュレータを作る際、メモリアクセス、I/Oアクセスは特別扱いしている。

例えば、Z80マシンのエミュレータ作る際、CPUとしてのZ80は同じモノなんだけど、メモリのアクセス(メモリマップ)はマシンごとに違っている。

この違いをメモリアクセス関数を変えることで対応している。

f:id:PocketGriffon:20211223140338j:plain

↑図がわかりにくくて申し訳ないが…(^^;;

 

例えばPC-8801の場合、Z80コアとPC-8801ハードウェアは切り分けて開発をしている。

Z80モジュールの初期化をする際、メモリアクセス関数、I/Oアクセス関数も(関数へのポインタとして)一緒に渡す。Z80側はメモリやI/Oアクセスは必ずその関数を通して行うため、実装自体はPC-8801の都合で作る…というワケだ。

 

PC-8801の場合、バンク切り替えなど(例えばSR以降に備わっているALUなども)のハードウェアに密接に絡んだ部分は、この関数がすべてを受け持つ。

 

MC68000の場合、メモリアクセスが3種類ある。1バイト、2バイト、4バイトのそれぞれアクセスだ。実際の関数は1バイトと2バイトアクセスを用意し、4バイトアクセスは2バイト関数を2度アクセスするようにしている。

 

命令実行サイクル

命令を実行した時、実行に何サイクル掛かったのかを記録していく必要がある。

別に実行速度を気にしているわけではなく、どのタイミングで割り込み処理を挟み込んで良いのかを知るためだ。例えばタイマー割り込み、垂直同期、水平同期などはこのサイクルカウントから生成する。

 

68000の命令は、基本となるクロック数に加えてアドレッシングによるクロックの増加がある。この辺りを正確にカウントするのは骨が折れる(T-T)

 

今の時点ではそこまで正確なクロック数は必要ないが、ちゃんとカウントするようにしないと水平同期によるラスタースクロールなどの実現が難しくなる。そういう機能をきちんと実装している世の中のエミュレータは本当にすごい(T-T)

 

1命令を実行する

メモリ16MBの確保をした!

MC68000エミュレータの初期化もした!

SSPとPCも初期化した!

ここまできたらあとはもう命令実行を開始するだけだ!(^-^)

#特定のマシンを実行する場合は、ハードウェアの初期化も必要になる

 

f:id:PocketGriffon:20211223145812j:plain

PCの位置から1命令(2バイト)の情報を読み出す

その命令が「何を意味しているのか」を解析する

実行する

その結果、メモリやフラグに反映させる

文章で書くととても単純だ(^-^)

 

00FF0010: 46FC 2700 4FF9 0000 2000 4E70 91C8 203C

 

まず命令コード(ここではメモリの$00FF0010の位置(PC)にある$46FC)に注目をする。このコードが何なのかを調べる事となるが、これの役に立つのがシリーズ「その2」に出てきた以下の図だ。

f:id:PocketGriffon:20211212125043j:plain

これらを聞いてピンと来ない方は「2進数16進数」という言葉を元にググって欲しい(^^)

これで見ると$46FCというコードは「MOVE to SR」という命令だと分かる。

この命令は、SR(ステータスレジスタ)へ値を代入する命令だ。

そしてこの命令の意味はこんな感じだ。

f:id:PocketGriffon:20211223151045j:plain

慣れていかないと、この図を読み解くのは少しむずかしい。

MOVE.{W} <ea>, SR

この<ea>というのが写真下の方にある「実効アドレス(データ・モード)」に置き換わる。

$46FCは2進数で「0100 0110 1111 1100」となり、下位6ビットに注目すると「111 100」、つまり実効アドレスの表に置き換えると「#Imm」となる。

68000CPUでのプログラミング経験がないと、おそらくここの文章の意味は通じない(^^;;

 

MC68000エミュレータで面倒な事のひとつは、この実効アドレスを解決するところかもしれない。ここで指定されたのがDnの場合はレジスタの中身を指すが、(An)などはレジスタの指すメモリの中身を指す。アドレッシングによる解釈がかなり面倒だ(T-T)

 

実を言えば、この実効アドレスを場合分けするのがエレガントには実装出来て無く、ものすごくベタにプログラムを書いている。もう少し書き進めてみて、抜けなくキレイに書ける方法が見いだせたら書き直してみたい(実行速度も気になるし)。

 

SRが16bit長であることから、イミディエイトの値は自動的にWORDとなる。

この命令(2バイト)の後ろにある2バイトがイミディエイト値となり、それは$2700だ。

アセンブラ的には「move #$2700,SR」となる。

 

f:id:PocketGriffon:20211223160526j:plain

ステータスレジスタは↑こんな感じの意味となっている。

$2700は2進数で「0010 0111 0000 0000」となるので、MC68000CPUの状態はスーパーバイザーモード、割り込み禁止という意味となる(^^)

 

今回はここまで

なーんとなく命令を実行するプロセスが伝わるだろうか(^^)

この1命令実行のプロセスを、すべての命令で実装していくのだ。おそらく読んだ方も「えーそれは面倒!」となるだろうが、実際に作ってみてもそう思う(^^;

エミュレータの作成はとても地味な作業が続き、かつ根気が必要だと思う。

 

こんな感じで全命令の説明をしていくわけにはいかないので、ブログをどうやって収めていくのか課題ですな…(-_-;

まだもう少し書かねばならない事もあるので、あと何回かは続くよ(^^)

 

ではまた次回!(^-^)ノ

MC68000エミュレータの開発 その4

f:id:PocketGriffon:20211217112618j:plain

今回は68000固有というよりは、私が行っているエミュレータ開発全般的な話を書こうと思う。相変わらず概念的な話にはなってもうしわけがない…。

 

あくまでも私の作り方であり、他のアプローチを否定するものではない事を強くお伝えしたい。参考程度で読んでいただけると助かります!(^^;;

出先でテキストを更新しているので写真少なめです。

あとでちょこちょこ追加するかも(^^;

 

現状のMC68000エミュレータ開発進捗

まずは先に開発中の68000エミュレータの進捗報告だ。

現在はリセットから398,348ステップ、命令グループは31まで実装した。

前回から出てたその数字の意味はなんだ…と言われそう(^^;

 

ステップというのは「リセットしてから何回命令を実行したか」という数字。

命令グループというのがとても説明しにくい…。例えばmove命令を1つのグループとして見立てている。move命令には数多くのアドレッシングが存在するが、そのうちの一部(の必要なアドレッシング)しか実装していない。命令としては未完なのだが登録はしてある…という感じで表現してる(分かりづらい…)。

 

実装している命令グループの割にステップ数が進んでいるのは、メモリクリアなどのループがたくさんあったからだ。テキストVRAMをクリアするだけで大量のループがある(^^;

 

私はエミュレータ開発中は、このステップ数を見ながらモチベーションを保ったりしてる。命令を追加するとどーんと進み、バグを修正するとどーんと縮んだりする(^^;

 

今くらいの開発状況だとバグを見つけるためには「プログラムの文脈を感じる」のがとても大事。なーんとなくプログラムの流れが変だと思ったら、そのまま進めるのではなく、プログラムを見直すのが大事な時期だ。納得出来ない事を放置してはいけない(^^)

 

バグを修正すると、それまで進んでいたステップ数がズドムと減る事があり、モチベーションも同時にドガンと減る(^^;; そんな事を繰り返しながら地道に根気良く作り込んでいくのがエミュレータ開発だ。

 

この数日間もエミュレータのバグのみならず逆アセンブラのバグなども見つかり、一進一退を繰り返している(^-^;

 

エミュレータの構造

エミュレータは大きく分けて2つの要素で成り立っていて、CPUそれ以外のハードウェアに分別している。おそらくほとんどの場合はCPUを先に開発していき、途中からハードウェアも同時に開発対応が進む。

マシンによっては「それ以外のハードウェア」がてんこ盛りの場合があり、X68000はまさにソレだ(^^;; 

 

今の私は68000CPUのエミュレーションを開発しているだけで、ハードウェア周りは完全な未実装状態。ハードウェアの大半はCPUと密接に繋がっているので、先にCPUエミュレーションを実装ししまわないてと話が始まらない(^^)

 

何度も書くが、今回のMC68000エミュレータCPUに限ったお話(^-^)

 

ちなみに……X68000の画像関係は実装がかなり大変だと思う。多彩なグラフィックモードも面倒だけど、グラフィックモードによるメモリアクセスの違いやBGやスプライトなどの対応、さらにラスタースクロールなどのタイミングがシビアなモノを作るのは想像しただけで厄介だ(T-T)

 

しかしX68000ではないが過去に似たようなものを作ったことがあり、ソコに対する技術的な興味がすでにない。画像関係とか作らないよーって言ってるのはいくつか理由があるが、コレも大きな理由のひとつだ(^^)

そもそも私自身がゲームで遊ぶ人ではないってのもあるんだけど(-_-;

 

CPU構造体

CPUの構成を超大雑把に言えば、レジスタ、フラグ、メモリを扱いながら与えられた命令を実行していく機械だ。これらの要素を言語的に定義をし、真似するプログラムを書いていく。

私はCPUをエミュレーションするための構造体を用意している。

C++ならばclassにしちゃうのが良いと思う。

その構造体の中に「扱うメモリ」「CPUのレジスタ」「メモリ読み込み関数」「メモリ書き込み関数」「I/O読み込み」「I/O書き込み」「その他」を定義している。

 

CPU構成を構造体にしている理由は、マシンによっては複数のCPUを持つ場合があるからだ。FM-7などはこの構造体が2つあり、メインCPU/サブCPUとして使っている。

PC-8801もメインCPUと、ディスク側にもうひとつZ80CPUが載っている。私が良く作っているアルフォスエミュレータではディスクをサポートしていないので、CPUは1つだ。

 

CPUレジスタ

CPUのエミュレーションをするためには、そのCPUについて詳しく調べておかなくてならない。可能ならば、そのCPUでのプログラミング経験もオススメしたい。

過去に、そのCPUでのプログラミング経験が無いままエミュレータを書いたことが(何度か)あったけど、思い違いなどがたくさん含まれてしまい、結構難儀した(T-T)

このブログでもPC-1350エミュレータは経験無しで開発している。

pocketgriffon.hatenablog.com

 

これが↓レジスタの定義。

f:id:PocketGriffon:20211217084540j:plain

MC68000ではDレジスタが8つ、Aレジスタが8つあり、どちらも32ビット長だ。型定義はそれっぽく読んでもらえるとありがたい。

 

CPU命令を実装していく流れで、これらのメンバを読んだり書き換えたりしながらCPUのマネをさせていく。エミュレータとは直訳すれば……真似?模倣?

 

実はユーザースタックポインタとシステムスタックポインタ、それとA7レジスタの関係性が分かっていない。A7レジスタがアクセスされた時は、ステータスレジスタのSビットを見て、どっちのスタックをアクセスしたのかを判断している。これであってるのかな…汗

 

メモリ読み込み、メモリ書き込み

メモリに対する操作をする関数を定義する。

f:id:PocketGriffon:20211217090554j:plain

いつもだと1バイト読み込み/書き込みの2つの関数を定義しているが、68000の場合は3つ(1バイト、2バイト、4バイト)のアクセスパターンがあったので、3つずつ定義した。

 

エミュレータのメモリアクセスは、この関数以外では絶対にしない。アクセスを関数に集中させる事で、バンク切り替えなどの構造定義を簡単にしている(^^) その分、速度的な犠牲はあると思うが、それはもう必要悪だ(悪?)。

 

現状のエミュレータではバスエラーを実装していない。

68000はメモリがない空間をアクセスしたり、奇数アドレスからのワードアクセスしたり不正にスーパーバイザーエリアをアクセスしたりするとバスエラーが出る。X68000のROMやHuman68kはバスエラーを起こさず実行ができてるのでであろう…という前提の元、チェックを省いてしまっている(^^;

最終的には実装しておきたい。

 

I/O読み込み、I/O書き込み

いわゆるハードウェアとのやりとりをするポートのアクセス関数だ。Z80などの場合はOUT/INなどのCPU命令でアクセスされるが、68系ではメモリマップドI/Oが多い。メモリにアクセスをするだけでハードウェアとのやりとりが出来るので便利だ!

 

今回の68000ではメモリに配置されたI/Oが並んでいるため、I/Oの読み書きは上記の「メモリ読み書き」から呼び出される構造になるだろう(まだ実装していない)。

 

この辺りを拡張していくと豪華なグラフィックやサウンド、キーボード周りが実装できるようになるはずだ。

 

CPUの初期化

一番最初にCPUの初期化から始めるんだけど、ここでネタを1つ。

 

CPUがメモリに置かれた命令を1つずつ解釈して実行しているのはご存知だと思う。そのための「プログラムの位置」をプログラムカウンタ(PCだったりIPだったり)が保持しているのもご存知と思う。

そのPC、リセット時にはどこのアドレスを指すのかまで知ってる人は多くない(^^)

エミュレータを作るためには、まずはそれが重要だ。

 

マシン語入門と書かれた本には、その手の情報はほぼ書かれていない!

多くはCPUのデータシート的な役割をしている資料に書かれている。

 

ちなみに

Z80:0000h

8086:FFFF:0000h(CS=FFFFh、IP=0000h)

6809:$FFFEに書かれた2バイトをスタートアドレス

というようになっている。

 

MC68000は$00000000に書かれている8バイトをSSPとPCにコピーをして起動する。

CPUそれぞれの起動方法があって実に面白い(^-^)

 

なんかだらだらと書いてしまった…汗

出先では調べられる事も限りがあて大した事が書けないな…(^^;

次回はもう少し具体的な実装方法をご紹介出来たらな…と思う。

 

ではまた次回!(^-^)ノ

PC-9801CV21でPC-8801を動かす!(過去作業)

MC68000エミュレータを作りながら昔のエミュレータ開発を思い出した。

今回はちょっと脱線して、過去に動かしたエミュレータを紹介してみたい(^^)

 

f:id:PocketGriffon:20211214204851j:plain

何年か前の年末年始お楽しみ作業で、PC-9801CV21(V30 10MHzマシン)でPC-8801エミュレータを動かした事がある。

 

そもそものはじまりは…

そもそも…なんでPC-9801PC-8801を動かしてみようと思ったのか。

それは友人からの「PC-9801CV21の小さなブラウン管で、アルフォスが動いてたら感動するね!」という一言から始まった!

 

PC-9801CV21のCPUは、V3010MHz

PC-8801のCPUは、Z804MHz

単純構造のエミュレータを作るのならば、エミュレーションする元マシンのCPUよりはせめて2〜30倍以上は速くないと厳しい。

経験則から考えても、V30でZ80を(現実的な速度で)エミュレーション出来る気がしない。

 

時間がたっぷりあるのならばJITコンパイラとか試してみたいけど、今回はそういう感じでもなく、とりあえず動かしてみたらどーなるのか…という遊び心から来てる(^^)

 

無理」と断るのは簡単だけど、ちゃんと検討した上で「無理」を突きつけたい!

だけど…ちょっとしたアイディアが浮かんでしまい、それを元に「もしかしたら作れるのでは?」という目線を元に、両機種を比較してみた。

 

都合の良い材料

グラフィックフォーマットが同じ

PC-8801PC-9801では、絵を表示するためのデータフォーマットが同じだ。VRAMが3プレーンあるのも同じ(PC-9801は機種によって4プレーン)。これはとても有利に働く。

VRAMのメモリアクセスさえ解決してやれば、書き込みの速度は高速で行えそうだ!

もひとつ、カラーパレットの設定データも両者は同じだ!(^^)

 

バイトオーダー(エンディアン)が一緒

これも両者ともリトルエンディアンなので2バイトアクセスなどで有利に働く。

 

メモリがたくさん(640KB)ある

現実的な速度でエミュレーションを動かすために、贅沢なメモリの使い方をするケースもあるだろう。余裕があればあるほどアイディアは実現しやすい(^^)

 

フラグの生成が簡単

8086のフラグ生成と、Z80のフラグ生成はタイミングも結果もほとんど同じだ!

 

フラグの保存が簡単

8086には「SAHF、LAHF」という命令があり、フラグの状態をAHレジスタに退避、復帰する事が出来る。これをエミュレーションの実装で上手に活用が出来ればとても便利だ!

 

都合の悪い材料

CPUが違う

これはいわずもがなの事実!

ただ8086は、Z80(というよりは8080)の後継アーキテクチャという事もあり、命令自体が良く似ている。勝機があるとすればここかも!

 

マシンアーキテクチャが違う

これももう仕方がない!

仕方ないけど、幸運な事に「PC-8801を進化させたらPC-9801になる」と思えるほど似ている部分もある。しかも今回はアルフォスが動けば良いと割り切れば…!

 

速度が足りない

初代PC-8801の速度はZ80の2.3MHzくらいと言われている。これはDMAが使われる関係でCPUが停止するタイミングがあるから…らしい。命令コードは最低で4クロック。

 

片やV30は10MHzノーウェイトの動作。命令コードは最低2クロックで実行できる。

概算で7.5倍くらいの速度差があるようだ。

 

しかしソフトウェアでCPUをエミュレーションするためには、まだ足りない。この速度差はどうしようもないので覚悟の上でチャレンジするしか無い!(T-T)

 

バンク切り替えをどう実現するか

PC-8801にはバンク切り替えという概念がある。VRAM、ROM、メインメモリ、それぞれをバンク切り替えしながらメモリアクセスを行う。これをPC-9801で実現するためにどうしたら良いのか…

実はこれが最初に思いついた「こうすれば…」というアイディアだった(^^)

 

8086にはセグメントという概念がある。これを上手に利用すればバンク切り替えっぽい事が出来るのでは…という考えが頭に浮かんだ。つまりこういう事だ。

 

メインメモリをアクセスした際のDS(データセグメント)

VRAMをアクセスした時のDS

ROMをアクセスした時のDS

 

それぞれのメモリをアクセスした時、DSの値を8086側の都合良い値にセットすれば、複雑なアドレス変換などを行わずにメモリアクセスが出来るはずだ!

説明が簡略的で申し訳ないが…

f:id:PocketGriffon:20211214235953j:plain

PC-8801のメモリマップを、

8086側↓ではこんな感じにした。

f:id:PocketGriffon:20211215000211j:plain

 

フラグ生成が微妙に違う

エミュレータの開発では、計算結果のフラグを生成するのが意外にも厄介だ。いつもだったら計算結果を元にソフトウェアでフラグ情報を作り出す。

これが…8086とZ80の関係性だとちょっとラクする事が出来る。

 

  SAHF ← フラグ情報のロード

  ADD AL,CH ← 通常の計算

  LAHF ← フラグ情報のセーブ

 

こうしておく事で、素の演算結果のフラグ状態をAHレジスタに退避する事が出来る。この仕組みを上手に活用する事で、エミュレータ上でフラグを作り出す作業をパスする事が出来る!(^-^)

 

だけど……各命令でフラグの変化を詳しく調べてみたところ、一部の計算でZ80と8086の互換性がない事が分かった。もはやどのコードがどうだったか…はソースを見ないと思い出せない…。

フラグの違いをちゃんと把握した上で実装していかないといけないので、かなーり気の抜けない作業となった。

 

もう少し検討

そもそもV30にはハードウェアで8080エミュレーション機能が存在する。これを上手に使うことで高速にZ80エミュレーションが作れないのか??

未定義命令を実行した時に、何らかの方法で検知する事ができれば目的は果たせそうだ。

…で、調べてみたんだけど、どうやらそういう機能はないみたいだった(T-T)

この方法はダメそうだ!

 

8086は典型的なレジスタマシンであり、メモリをアクセスすると途端に遅くなる。そのため、Z80エミュレーションを動かす時に、可能な限り実メモリをアクセスしない方法で実装しなければならない。

レジスタ構成を考えてみて、以下のようにしてみた。

f:id:PocketGriffon:20211215002546j:plain

表の見方は、Z80のAFレジスタはV30側でCXレジスタに割り当てた…という感じだ。

Z80のIX/IYなどはメモリに置かれる。プログラム的にIX/IYは出現が多くないので優先度を下げた。R/Iレジスタも同じ。SPだけはレジスタに置けないのが気になるが、レジスタ数の関係でどうにもならなかった。

 

表が間違っていたので訂正をば。PCがBPに割り当たっているが、SIの間違いだ(T-T)

 

そしてレジスタ割り当てはイッパツで決まらなかった。

一度PC-8801が起動するまで作り込んだが、どうにもしっくり来なくて作り直した!

その結果、上記の割り当てとなった(^^;

 

開発作業

これまで検討した事を考慮しつつ、エミュレータを開発していった。

実行速度を重視するため、大半のコードをアセンブラで書く事となった。ファイルの読み込みなどのアセンブラで書くと面倒な部分はC言語で記述したが、それ以外は初期化も含めてアセンブラとなった。

最終的にはZ80PC-8801ハードの実現で、約5700行のアセンブラコードとなった。

 

開発はMacで編集、Mac上のDOSBoxでビルド、出来上がったファイルをフロッピーディスク経由でPC-9801NX/Cへ送り込んで動作確認を行った。

PC-9801NX/Cはアクセラレータを積んでいて486SX 40MHzくらいの速度があったので、動作確認もだいぶ楽に出来た。

最終的にはPC-9801CV21で動かしたが、実際に動かした回数は数えるほどで済んだ(^^)

 

まずはPC-9801上で、Z80で動くN88-BASICが動くようになった。

言葉が似てて説明がややこしいけど頑張ってついてきてw

f:id:PocketGriffon:20211215004316j:plain

BASICのプログラムがちゃんと動いた!

 

この段階ではPC-9801のハードウェアには依存していなかったので、試しにHP200LXでも動かしてみた。

f:id:PocketGriffon:20211215005154j:plain

HP200LXで動くPC-8801エミュレータ

これは……多分、絵面的にとても面白い!(^-^;;

ちなみに実行速度はびっくりするくらい遅かった。

 

速度の問題は置いておくとしても、まずは動かす事が大事だ!

どんどん作業を進めていくと、ようやくPC-9801NX/C上でアルフォスが動き始めた!

最初の画面がこちら!

f:id:PocketGriffon:20211215010228j:plain

…なにこれ?とか思わないで!(^^;;

テキスト画面を非表示にしていないのと、カラーパレットを合わせていないだけだ。

画面の右下辺りをじっくり見てみると「Morita」「NAMCO」という文字列が見える!

f:id:PocketGriffon:20211215010551j:plainf:id:PocketGriffon:20211215010827j:plain

テキスト画面がなければある程度見える。

この当時は写真の撮り方も雑で、手と携帯が写っちゃってる(^^;;

 

f:id:PocketGriffon:20211215011057j:plain

カラーパレットを合わせてやればPC-8801と同じ画面となる!

PC-9801では標準で640x400ドットだが、アルフォス起動時に640x200ドットモードに切り替えている。ソフトウェア的に頑張らなくてもPC-8801と同じ画面が出た。

 

この状態でPC-9801CV21で動かしてみたのだが……

f:id:PocketGriffon:20211215011749j:plainf:id:PocketGriffon:20211215011809j:plain

めっちゃくちゃ遅い!!!!

データのロードに2分、タイトル表示→ゲームまでも2分。

ゲーム画面は秒間2コマくらいしか出ない状態だった!!

動いたよ…動いたけど、これは動いたと言って良いのだろうか…(-_-;;

 

鬼の高速化!!

ここからお楽しみの高速化を始める!

 

なんとなく8086マシンでも動かす事を考えていたので、V30特有の命令を使わないでいた。しかしそうも言ってられなくなったので、この先は積極的に使う事にした!

 

垂直同期割り込みの要因を作るために各命令を実行した時に掛かったサイクル数をカウントしていたが、これを全廃止。

 

そして割り込みタイミングを固定化してしまった!

さらにゲーム開始時にテキストVRAMを非更新、非表示とした!

 

これでもまだ速度が足りない!!!

 

LDI、PUSH命令が連続する場合には特別処理をするようにした!

スタックポインタが更新されるまでスタックセグメントは変更しない、同じく長距離ジャンプがない限りはコードセグメントを更新しない、などなど細かい対応もしてみた!

 

最後はZ80の命令を拡張する暴挙に出た!(^^;

LD A,(HL) : INC HLなどの典型的に出てくる命令を新設1命令に置き換え!

もっと行くぜと32x16ドットのクリア、32x16ドットの描画を行う専用命令を新設!(^^;

 

よーし、これでだいぶPC-9801CV21でも動くようになった!

なったけど…命令を新設する時点でもうエミュレータじゃない(^^;;

この辺りが潮時…とばかり、作業終了とした。

ここまでの開発期間は、途中の作り直しも含めて20日間でした(^^)

 

番外編

せっかくV30で動くZ80コアが出来上がったので、他にも何か動かしたいな…と。

なんとなく資料を見ていて気がついたPC-9801のテキスト画面のセミグラフィック。

これってPC-8001のグラフィックと同じフォーマットじゃん…と!

 

そんな事情で作ってみたPC-9801で動くPC-8001エミュレータ

f:id:PocketGriffon:20211215013416j:plainf:id:PocketGriffon:20211215013441j:plain

f:id:PocketGriffon:20211215013524j:plainf:id:PocketGriffon:20211215013545j:plain

一見してグラフィックを表示しているように見えるが、これは全部PC-9801のテキスト画面で表示している。PC-9801PC-8001ではアトリビュート情報の互換性がないため、その部分のみPC-9801の都合に合わせた変換をした。

全体的な処理が軽いので、PC-9801NX/Cでは実機よりもかなり速く動く!

 

 

f:id:PocketGriffon:20211215013956j:plain

テキストでPC-8001が動くという事で、↑ネタ画像ではあるけどこんな絵も出せる!

テキスト画面でPC-8001、グラフィックス画面にPC-8801を表示してる。この状態で両方が動くわけではないが、こんな表示も可能ですよって事で(^-^)

 

終わりに

今回のブログは、何年も前に作ったもののご紹介でした!

以前、別のアカウント名で公開してたので、見た覚えがある人もいるだろう。

当時はブログを書いてなかったので詳しい説明が出来なかったが、今回ようやくこうして文章として残せる事になった!(^-^)

 

PC-9801CV21で動いてるアルフォスを見たい…という、些細な話から始まった制作だったけれど、私個人としてはV30でZ80のエミュレーションコードを書くチャンスともなった。開発当初は半信半疑ではあったけど、ちゃんと動いたので大満足だ!(^^)

こういう無茶なチャレンジは本当に楽しいね!(^o^)/

 

ではまた次回!(^-^)ノ

MC68000エミュレータの開発 その3

f:id:PocketGriffon:20211213093431j:plain

今回はMC68000の逆アセンブラについて書いていく。

最初に白状してしまうと、この逆アセンブラはキレイに作れた感がまるでない(^^;;

むしろ力技で作りきったというか。

困ったことにもう一度作ったとしてもエレガントに作れる気がしない(-_-;

それを踏まえた上で、読み物として流してくれると助かります!!!←力説

 

最初に余談!

私は若い頃、ハンド逆アセンブルをしていた期間がかなり長い。さんざん逆アセンブラをしてから「これってパソコンで自動で出来ないのかな?」と考えた事があった。

 

BASICで逆アセンブル出来るプログラムを書いてみた。アルゴリズムも何もない、このコードが来たらこう表示するってだけのプログラムだった。ほんの数命令しか逆アセンブル出来なかったが、相対アドレスを計算しなくても済むように出来たのが嬉しかった!

ちょっとしたプログラムだったけど、すごい達成感があって「もしかして…このままマシン語をマスターしちゃう?!」とか自惚れたのを覚えている(^-^)

 

あと、逆アセンブラ=ディスアセンブラという事を知らず、世にたくさんある事も理解出来てなかった(^^;;

なんだ、それだったらI/Oにたくさん載ってるじゃん…と後になってから萎えまくったのは、若い時代のほろ苦い思い出w

 

センシティブな内容だぜ!

先日から私のツイートに「センシティブな内容が含まれている可能性があるため、このツイートに警告を表示しています」と出てしまっていて、Twitterアプリを使ってる方からはツイートが見えなくなってるみたい。

 

公序良俗に反する事なんぞ書いてないと思っているのですが、どうやら「エミュレータ」という単語に反応しているのではないか…と見解もいただきました。なるほど…それだとしたら引っかかるけど、解除してくれるのかな…これ(T-T)

いきなりアカウント削除されたらすみません(ありうるので怖い)。

 

アセンブラ

当初、「逆アセンブラとは」みたいな項目を書いていたけど、すっきり説明ができそうになかったので、ネット上にある優秀な文献に任せる事にした!←

ここでは逆アセンブラとはなんぞやが理解してる人に向けて話を進める!

進めるったら進める!(^-^)/

 

f:id:PocketGriffon:20211213120359j:plain

↑こんな感じのバイナリのプログラムをもう少し人間が分かりやすいように変換してやると…

f:id:PocketGriffon:20211213093431j:plain

↑こんな感じになる!

 

アセンブラを見たことがない人には「これでも全然わからんのですけど…」となると思うが、そこはホレ、入門書とか読んでみて理解を深めてもらいたい(^^;;

または「ふーん、そういうもんね」と流してもらえたら(^^)

エミュレータを作ろうとした場合、アセンブラ表記の解読は避けて通れない。

 

表示をどうするか!?

まず、最初にこだわりたいのは「どんなふうに表示するか」だ!

エミュレータ開発中はずっとずっと見る逆アセンブラだ、ここにこだわらなくてどーする!

場合によっては数万行ある逆アセンブラリストと格闘する事だってあるんだぞ(^^)/

 

というわけで、私はこだわりなかったのでX68000のSCD表記を真似してみる事にした(をぃ

うん、こだわりとか気にせず考えずに作っちゃってるね(-_-;;

ニーモニックに付くサイズ指定もあったりなかったり…未完成感がすごい。

これはこの先の開発を進めならが修正していく(moveaには.wと.lを付けたい、など)。

 

最初は、参考にしたもの、または使い慣れた表記に合わせてみるのが良いと思う。

私はX68000のSCDで慣れてしまっていたので、この表記が見やすい。

人によって違いはありそうなので、製作者のこだわりでどうぞ!(^^)

 

上の逆アセンブラ表示で、バイト列とニーモニックの間がどどーんと空いてる。

これは68000の命令長が最大で5ワード(10バイト)あるから、その分を空けてあるのだ。

最大の長い命令が来てもキレイに表示されるよ!

 

もうひとつ、レジスタの表示をどうするか。

私はこんな感じで表示させてみた。そこはかとなくSCDに似てるのは気のせい…かも?

f:id:PocketGriffon:20211213133721j:plain

ニーモニックの下に表示されるレジスタが、実行後のレジスタを表示している。

なので実行前のレジスタニーモニックの上に表示されてるモノを見る必要がある。

これは過去にどんな経緯でマシン語人生を歩んできたかで表示する順番が変わりそう(^^;

 

[SUPER]というのは、X68000 SUPERを意識してるワケではなくて、MC68000の動作モード(スーパーバイザー | ユーザー)の表示だ(^o^)

 

アセンブラを呼び出す関数をどうするか!?

アセンブラを使う側としては「アドレスを渡したら、そこにある命令コードを何も考えずに逆アセンブルして表示してくれる」のが楽ちんで良い。

 

アセンブラ単体でテストする場合には、アドレスが更新していかないと都合が悪いけど「その命令の長さ」が分からないので、次の命令を指す事が出来ない。

 

というわけで、逆アセンブラ関数からの戻り値としては「今逆アセンブルした命令の長さ(バイト長)を返す」ようにしたら良さそうだ。

……意味通じた?(^^;;;

 

  uint32_t pc = 0x00FF0000;
  while(1){
    pc +=逆アセンブラ関数(pc);
  }

 

こんな感じにすれば永遠と逆アセンブルする事が可能だ!

 

命令デコード

アセンブラを作る上で、おそらく一番肝心な部分であるデコード。

ここがキレイに作れるかどうかで、その後の難易度がどどーんと変わってくる(T-T)

 

まずはありがちな単純デコード。

単純明快に命令の数分だけ関数を並べてしまうやり方だ。

void (*func[256])(unit32_t);

8080、Z80、SC61860、65816などはこんな感じでテーブルを用意した。

1980年代ならサイズ的に躊躇する方法だけど、現代のコンピュータであればメモリもCPUパワーも問題にはならないので、解決策のひとつだろう(^^)

 

ちなみにswitch〜caseを256個書いても、gcc(コンパイラ)がジャンプテーブルに直してくれる場合がある。それを見越した上で書いていくのもアリだ!

 

しかしMC68000でこの方法は難しい。

命令コードが2〜10バイトで最短長が2バイト。2バイトということは最低でも65536の組み合わせがある。テーブルジャンプなんぞ作ったら65536 * 8バイト(最近の64bitマシンだとアドレスは8バイトある)で256KB!テーブルだけでこのサイズいただけない(^^;

 

かといってswitch〜case文で65536を分岐する方法もさすがに避けたい(T-T)

もうちょっと違う方法を考えてみよう。

 

f:id:PocketGriffon:20211213162652j:plain

例えば……MOVE命令とそれ以外を見てみると、MOVEはbit15/14が必ず00になるらしい。しかし、15/14が00になる命令は他にもたくさんあるため、MOVE命令を先に判断してしまうと失敗しそうだ。

 

ORI to CCRなどは16bitの値が固定($003C)になっているため、単純な比較で調べる事ができる。

ということは、比較するビットとマスクを調べ、マスクのビット数が多い順に並べてやったら上手くいくのでは…(^-^)

 

f:id:PocketGriffon:20211213165714j:plain

こんなデータを用意してみた。

メモリから拾ってきた命令コードと1つめのデータをマスク、その結果と2つめのデータを比較して、同じならば関数へ飛ぶ…とすれば、目的が達成できそうだ。

問題は、命令を調べるごとにテーブルの最初から最後まで検索する可能性があるため、処理負荷が安定しない事だろうか…。

f:id:PocketGriffon:20211213171328j:plain

コード的にはこんな感じ?(実行は別の場所でやってる)

 

分岐先では各ビット情報を元に解析をしまくっている。

おそらくこの処理も重要なポイントなのだが、残念ながらこの部分は力技で作ってしまった。もうちょっと工夫すればよかった…(^^;;

 

表示をより簡単に!

表示が確定した内容を、確定したタイミングでどんどん表示していく方法もあるが、私は最後の最後にまとめて処理するようにしてる。

・アドレス情報

・バイナリ情報(2〜10バイト可変)

ニーモニック

オペランド

オペランド

分岐先の各関数では上記の文字列を埋めるようにしておいて、最終的に良い感じに整形しながら表示するようにしている。

 

表示処理がたくさんあると、あとでフォーマットを変更しようとするとホントに大変!この辺りは何度も何度も作ってるうちに学習した(^^;;

 

開発したMC68000の逆アセンブラのプログラム行数を数えてみたら1105行だった。

力技で解決しなかったら、半分くらいで作れるかもしれない??(^^;;

エミュレータがある程度動くようになってきたら検討してみようとは思う(やる?)

 

-----

 

なんか…今回はめちゃくちゃ概念的な話になっていて、こんなんで逆アセンブラの理解が深まるだろうか…心配だ(T-T) ソースコードを合わせて出して行こうかと思うが、そうするとめっちゃプログラム語になっていきそうで躊躇してる。

そういう回があって良いのかもね!(^-^)

 

現在の進捗は、X68000のリセット時から3559ステップ実行、命令グループは17まで実装した。頑張ってる割に進まないな…ぐぬぬ(-_-;;

 

ではまた次回!(^-^)ノ

MC68000エミュレータの開発 その2

f:id:PocketGriffon:20211212111742j:plain

なんとなく大々的に始めてしまったMC68000エミュレータの作成。

Twitterに投稿をしたらたくさんリツイートされてしまって焦る…(-_-;;

見てる人は少ないだろうから、身内だけでこっそり進めたらいいや…という大前提が崩れてしまったかも!←じゃあ書くなって…(-_-;;

 

ちょっぴり責任感じつつ慎重に進めていくよ。

なお、作業スピードは遅めでいくと思うので、気長にお付き合いくださいませ(^^)

 

まずは事前調査

戦略において敵(敵!?)を知ることはとても大切な事だと思う。

作り始める前に十分検討をしてからでないと途中でプログラムが破綻する可能性がある。そのくらいエミュレータ作りは面倒だ!(T_T)

エミュレータ作りに必要なのは根気だと思う。

私に一番似合わない言葉だ!!!(TOT)

 

まずは手元にある、ありったけの資料をかき集める。

f:id:PocketGriffon:20211212132717j:plain

…と意気込んでみたけど、68000CPUの資料は多く持ってない事に気がついた! X68000の資料はたくさんあるんだけどなー(T-T)

過去にX68000でプログラミングをした際には、青本(68000プログラマーズ・ハンドブック)のみで事足りてしまった。この本はとても良いと思う!(^^)

 

資料を沢山用意する理由は「こっちの本に書いてある事とあっちの本に書いてある事」が違ってる場合が多いのだ!誤植の可能性もあるし単なる勘違いもありそう。そういう場合はいくつかの本を見ながら多数決で決めたりする(^^;

 

「使う分にはこれで十分な情報なんだけど、作る分には足りないんだよ!」って事も往々にある。 手元に実機があるのならばデバッガを立ち上げて実際に動かしてみるのが良い。

68000ではそういうテストをする機会がとても多い!

 

資料が揃ったところで、まずは逆アセンブラを作る事を検討する。

主な目的としては2つ。

マシン語の命令フォーマットを理解することで、エミュレータ作成時の命令デコード(このバイナリが何という命令なのかを解釈する)処理の検討が出来る。これはとても大事!

 

力技でデコード出来るCPU(8080などは256個のジャンプテーブルを用意)は特に考える必要はないけど、68000のような複雑な命令フォーマットを持つCPUでは必須だと思う。

 

2つめは、エミュレータを作り始めたら、必ず逆アセンブラが必要になるからだ。実装しようとしている命令が何なのかをバイナリで見ていく事は困難なので、そこで逆アセンブラが大活躍をする。

個人的には、エミュレータと逆アセンブラはセットで開発が必要だと思う。

 

まずは68000の命令形態について調べてみる。

f:id:PocketGriffon:20211212125043j:plain

いつもの流れだけど、私はこんな感じで書き出すようにしている。

見ても理解出来ない覚えられないの脳みそなので、自動車学校の先生に言われた「読んだって覚えん!書け!書いて覚えろ!」をいまだに真に受けて、ちゃんと書いている(^^)

 

f:id:PocketGriffon:20211212131256j:plain

まだまだ続くけど、全命令分でこれをやってみるわけだ(^^)

そうすると途中から共通点が見えてきて、脳内で命令デコードのプログラムが組み立てられていく。

 

こうして見ていくと、68000のエレガントな部分と、そうでない部分が見えてくる。

エレガントな部分はプログラムを実装する時にもキレイに書く事が出来るケースが多いが、そうでない部分は「そうでないと意識した」上で、対応が必要となる。

 

例えば一例を上げてみるとコレ。

f:id:PocketGriffon:20211212134733j:plain

これはmove命令の機械語フォーマットだ。

これによると、↓サイズの指定はこうなってる

f:id:PocketGriffon:20211212134806j:plain

2ビット(4種類)ある中で「無効、バイト、ロングワード、ワード」となっている。

 

これと比較するカタチで今度はこちら。

こっちはadd命令の機械語フォーマット。

f:id:PocketGriffon:20211212135100j:plain

これのサイズ指定は……↓

f:id:PocketGriffon:20211212135131j:plain

なんとサイズ指定の互換性がない!!

「バイト、ワード、ロングワード」という言葉だけでこの図を見ていると気づけない(T-T)

 

もっともコレは「OPモード」の指定であり、サイズ指定でないので別物だ、というのがモトローラ側の見解なんだろうけれど、作る側からみたらサイズはサイズだ(T-T)

 

こんな感じで普段アセンブラで68000コードを書いていると気が付かないような違いが見えてくる。これらを見落としていると、あとで実装が厄介だ。

 

バイトオーダー問題

普段からIntel系やARM系のCPUを使っていると、68000CPUで気になるのがバイトオーダー問題。いわゆるエンディアンというヤツだ。

「それなに?」という方は検索してもらうとして、一言でいえば「データを格納する順番の違い」かな…。

 

アセンブラを作る上ではそこまで問題にはならないんだけど、エミュレータを作る時にどう扱うのかを検討しておいた方が良さそうだ…。

 

普通に考えたら「メモリ中のデータはそのまま、メモリからレジスタへロードするタイミングでデータをひっくり返す、ストア時も同様」とするのかな。6809の実装ではそうした。

その場合、データのロードストアは頻繁に発生するので、高速に変換が出来るのかを調べておく必要がある。もしもひっくり返すのに大きなコストが必要だった場合には、最悪アプローチの変更だって検討しないといけない。

 

C言語でバイトオーダーをひっくり返すコードを書いて、アセンブラコードを確認する。

f:id:PocketGriffon:20211212151352j:plain

なるほど、どうやらARMプロセッサにはrev/rev16というバイトオーダーをひっくり返す命令が存在するようだ!これなら低コストで行けそうだ(^-^)
こんな感じで、不安要素を1つずつ減らしていくのが大事。

 

やはり詳しく書いていくと長くなるね…(^^;;

次回は逆アセンブラを作った時の事を書こうかなーと思う。

 

ちなみに現状の進捗は、X68000のリセット時から2006ステップ実行、命令グループは約13個を実装したが、必要な部分のみの実装となっているので、MC68000CPU単体で見ても1〜2%程度の完成度だろう(T-T)

 

ではまた次回!(^-^)ノ

 

MC68000エミュレータの開発 その1

f:id:PocketGriffon:20211210215441j:plain

のっけからものすごいタイトルで始まる今回のブログ(^-^)

しかも全く完成していない現状からブログを書く危険も伴う!!

 

まぁこのブログを読んでくださってる皆さんは、私が猛烈に飽きっぽいのはご承知の上だと思う(ご承知だよね?(ToT)) 出来る限り飽きないように、ブログに報告しつつ進めてみようというセコい魂胆だ!

と書きつつも、過去にV20エミュレータは飽きて放置してる前科がある!!

罪を重ねるのか、それとも汚名返上できるのか…(^^;

生暖かく見守ってもらえれば……(がくぶる

 

なお68000CPUと書きつつも、X68000っぽいものを作ろうとしてます。完璧なソレではないため、なんとなくMC68000という書き方をしている…と思ってください(^^;

 

そもそもなんで作るの?

68000CPUのエミュレータを作ってみようと思ったのには理由がある。

まず第一に、昔っから68000エミュレータは書きたいと思っていた。8080、Z80、6800(6301)、6809、8086(V20)、SC61860などなど…いろんなエミュレータを書いてきたけれども、68000だけは一度も書いたことが無いのだ。

避けていたわけではなく、チャンスが無かっただけの話(^^;;

f:id:PocketGriffon:20211211083824j:plain

もうひとつ、最近の私のブログにX68000が登場しているけれど、あれ以降何も進んでいない。実機で開発を進めるのでは無く、出来る限り開発環境をホストマシンで作りたい希望がある。しかし実行するXファイルには中に再配置情報というモノが含まれていて、どうしてもソレを作る事が出来ない…。Zファイル(絶対アドレスファイル)は作れるので問題ないじゃん…と思うが、それじゃあ……なんとなく…面白くない!(^^;;;

 

そしてみっつめの理由。

すでにX68000エミュレータがいくつか存在するけれども、手元のMac/DevTermでどれも動かす事が出来なかった…。なぜ他で動いてるのにウチでは動かないのかー(TOT)

素直にWindowsで動かせばいいじゃんとも思うが、それもちょっと違う気がする(T-T)

 

こんな感じでいくつかの理由が合体して、今回の「作ってみるか」に繋がった!

この機会を逃したら、一生作らない自信がある!!(^^;;

 

何を作ろうとしてる?

勘違いされてしまうと困るので先にお断りをしておくと、完璧なX68000エミュレータを作るつもりはありませぬ(^^;

 

やりたいことは「Xファイルを作れる環境が欲しい」、これだけだ。

理想をいえばgcc、as、lkが動く事…。makeも動いたら嬉しいなぁ…なんて思うと、Human68kが動く必要がある?

今後もおそらくやらないだろうと思ってるのは、市販のゲームを動かそうとする事だ(^^)

f:id:PocketGriffon:20211211084053j:plain

もう少し細かく見ていってみよう。

もしかしたらものすごく甘い事を書いてるかも知れないが、それは無知の為せる技!(^^;

 

ハードウェア部分

メモリマップはX68000そのもの。

サポートするハードはテキストVRAMとRTC(読み出し)くらいだろうか…汗

もしかしたらキーボードの一部もサポートするかも??

それ以外は極力ノーサポート。

 

IOCSコール

DOSCALLまたはHuman68kから呼び出される最低限の部分のみをサポート。ハードウェアをエミュレーションというよりは、C/C++でそれっぽく実装。

 

DOSCALL

基本的にはIOCSコールをするか、内部で処理されるものが大半だと思う。

おそらく多くはMC68000エミュレーションで動いてしまう気がしている。

 

Human68k

大半がMC68000エミュレーションで動く?CUI部分を使うかどうかでサポート範囲が変わってくると考えている。AUTOEXEC.BATをうまく組み合わせる事で、CUIを作らなくても済むのであればそうする。

 

……こんな感じで、X68000の特徴とも言える豪華なグラフィックやサウンドは一切サポートしないという割り切り!標準出力に出てきた文字を処理する事ができれば、実行時のウィンドウすら開かなくていいや…とさえ思っている!

 

ホストマシン上(私の場合はMacだったりDevTermだったり)でXファイルを作ることが出来る事のメリットは(自分の中では)計り知れない。

その後、動くのを確認したのちにあれこれ拡張したって構わないとも思うので、そこはもう気力次第だ(^^)

 

現状の進捗

いま現状では逆アセンブラが完成(と言っても力技で作った情けない版)しているのと、68000の命令が5つほど実行出来る程度だ。

 

エミュレータは最初の動作までがとても大変だ。

ここまでにレジスタの管理、メモリのアクセス、命令のデコード、命令の実行とフラグの反映方法などなど、非常に細かくて面倒な事を検討しておかなければならない。

 

特に68000は命令フォーマットが複雑なため、デコード部分をしっかりと考え抜いたあとで実装しないとひどい目にあう(^^;;

実を言えば1500行くらいコードを書いてから構造が破綻し、作り直してる!(^^;

f:id:PocketGriffon:20211211084248j:plain

エミュレータの作り方は人それぞれだと思うけれど、私はまずは逆アセンブラから作るようにしている。これは命令フォーマットを理解するのにうってつけだからだ。

アセンブラを作る難しさは、エミュレータのデコード部分を作る難しさに直結すると思う。

 

経験した中では、8080やZ80は逆アセンブラを作るのが簡単、6809は中くらいの難易度、難しかったのは8086やHD61700。特にHD61700は投げ出そうと思うほど大変だった!(ToT)

そして68000も結構大変で地味な作業が多かった。

これは…エミュレータを作るのが大変だという事を示唆している!!(T^T)

 

次回以降、さまざまな問題をどのように解決して進んでいこうとしているのかを詳しく書いていってみようと思う(^-^)

その流れで「これ、間違ってるよ?」的なご指摘があればとても嬉しい(^^;;

 

ではまた次回!(^-^)ノ

DevTermを使ってみる! その2

f:id:PocketGriffon:20211204142553j:plain

DevTermが我が家にやってきて以来、これでもか!というくらい遊んでる(^^)

実用的な使い方をしているとは言えないが、今のところは「これは出来る、これが苦手かも」みたいな感じで実験を繰り返してる。なーんとなくではあるが、得意不得意が見えてきた。

 

office互換ソフトのインストールなど、実用で使うには必須?のソフトを入れてないなど、使用目的の角度が違うかも(^^;;

実際、他の人は何をしたくてDevTermを手に入れたんだろうなぁ…(^-^)

 

X68000エミュレータを動かしたい!

ゲームを動かしたいというよりも開発環境を手元に作りたい希望から!

Linuxで動くX68000エミュレータはないかなぁ…と思って探していると、RetroArchという名前が検索に引っかかってきた!

MAMEという有名なエミュレータ環境があるのは知っていたけれど、RetroPieとかRetroArchというのが存在している事を知らなかった。

 

とにかく動けばいいや……そんな感覚でRetroArchをインストールしてみたが、残念ながらX68000エミュレータコアが見つからないという状態に(T-T)

どうやったらどうなるのかが分からず、これはここで中止。

 

次にソースが公開されているpx68kをインストールしてみる事に。

GitHub - hissorii/px68k: Portable SHARP X68000 Emulator for PSP, Android and other platforms

 

ソースが公開されているのならばビルドさえ通れば動くだろう…と思い、作業を始めたのだけれども…どうも完成したバイナリが動かない。

画面は真っ暗、マウスカーソルを動かすとデバッグメッセージのようにマウス座標が表示されるだけ…という状態だった(TT)

 

ネットで探してみるとDevTermでX68000エミュレータを動かしている方がいたので、なにか方法があるんだろうけれども…。ぐぬぬ、どなたかヒントください(T^T)

f:id:PocketGriffon:20211207000850j:plain

↑黒ウィンドウが表示されてマウスを動かすと座標が表示されるのみ…orz

 

MSXエミュレータを動かしたい!

ゲームを動かしたいというよりも開発環境を(以下略

 

MSXエミュレータといえば、1990年代にfMSXというのを動かした記憶がある。X-Windowで動くバージョンだったけれど、当時の環境(68030だったかRISCだったか記憶が曖昧…)でそれなりの速度で動く事に驚いた。

 

Z80の命令実装部分がswitch〜caseで書かれていて、最適化オプションを付けてコンパイルすると、えっらい時間がかかった記憶がある(^^;;

 

今はどうなってるんだろう…と思い調べてみると、今でもfMSXありますね!

それと同時に、他のエミュレータも見つかった。

そんな中で……今回はOpenMSXを動かしてみる!(ここまでのfMSXの流れはなんだったのかw)

 

パッケージの中にあるdoc/manual/compile.htmlを読みつつ必要なライブラリを揃え、ビルドしてやったら実行バイナリが出来た!出来たけど…ビルドが終わるのに3時間以上も掛かった(ToT)

なぜこんなに時間がかかるのか…(T^T)

f:id:PocketGriffon:20211207001341j:plain

意味ありげなキレイな絵が出てくると嬉しい!(^-^)

まだ実機からシステムROMを引っこ抜けてないので、今のところは互換ROMで動かす。

 

f:id:PocketGriffon:20211207001445j:plain

過去にfMSXを動かした際に読みだしたROMデータを読み込ませてみたら、無事に動いた!

この他にもカセットは何本?何十本か手元にあるけど、ROMデータ化してない…汗

 

MSXで吸い出しプログラムを書かねばなぁ…。過去に一度作ってるんだけど、当然そのソースは当時のフロッピーとともに失われてる。

確かスロット切り替えでデータ読んでフロッピーに書いて…とかした気がする(^^;;

 

FM-7エミュレータを動かしたい!

少し前にM5StackやRaspberryPi PicoでFM-7エミュレータを動かしている。

 

 

これは何年か前にLinux上で開発してあったソースを改造して動かしたものだ。

 

このFM-7エミュレータを開発した当時は「画面なんて確認程度に表示されればいいや…」的な発想しかなく、/dev/fb0をmmapして直接書き込む手法で画面表示を行っていた(^^;;

 

この機会にSDL対応をして、DevTermでも動かせるようにしてみた!

フレームバッファを生成するところまでは全く同じで、その結果をfb0へ出力するのか、SDLのバッファへ渡すかの違い程度の変更で、難なくMac上で動いてしまった。

 

そしてそのソースを何も考えずにDevTermへ転送→make、で動いた(^^;;

MacLinuxの移植性の高さをここにきて再認識する。すごいなぁ!(^-^)

 

ちなみにビルドに掛かる時間は、M1 Macだと1秒未満、DevTermでも5秒程度だ。

やっぱりそこまで遅くないよね、DevTerm。

なんで他のビルドにはあんなにも時間が掛かるんだろ…依存関係を調べるためのSDカードからの読み込みが遅いんじゃないか疑惑が…(^^;;

 

f:id:PocketGriffon:20211204142553j:plain

エミュレータを動かしてみて分かったのだが、動作速度がM1 MacよりもDevTermの方が速い。「あれ?速いね」という感じではなく、明らかな違いをはっきり感じるレベルで違う。

これまた不思議な現象…(TT)

 

レガシーな基板を動かしたい!

過去にV20-MBCという基板にドハマリし、壮大な大脱線をしながらも楽しんでいた!

この基板、Macでしか表示をさせてみたことがなかったけれど、当然Linuxマシンでも動くよね…と思って試してみた!

f:id:PocketGriffon:20211207004111j:plain

なんか……とっても良い!!(^o^)

PC-8201につなげてるような感覚に陥る!!(^^)

 

実際にこれで運用するかと聞かれれば多分しないんだろうけれども、それでも問題なくシリアルが繋がる実験にはなった!

こういう写真をたくさん残しておきたいね!(^^)b

 

PC-9801エミュレータを動かしたい!

ゲームを動かしたいというよりも開(以下略

英語圏内で作られたマシン上でPC-9801を動かすのはロマンだろう!(^^)

 

GitHub - AZO234/NP2kai: Neko Project II kai

↑今回はこちらのソースを使わせていただいた!

 

f:id:PocketGriffon:20211207072139j:plain

どのマシンをインストールする際にもそうだが、ライブラリの条件等を合わせつつ何度も何度もビルドを繰り返す。インストールしてあるはずのライブラリが無いと言われたり結構大変だ(T-T) このPC-9801もビルドを通すために散々苦労した…。

 

ようやく動いたPC-9801エミュレータ

ここまで動かしている過程で、そういえば漢字ROMを引っこ抜き忘れていた事に気がつく…。

どのマシンでも実機のROMを用意するのが面倒だ(T-T)

みんなよく頑張ってるなぁ…(そういうツールがついてるんでしょうねきっと)。

 

CPU動作環境の変更

DevTermのCPUは6コア入っていて、通常時、ビルド時、その他等でCPUの動作状況を変えながら動かしている。

デフォルト状態では4つのコアが動いてる状態らしい。

f:id:PocketGriffon:20211207073201j:plain

この状態を見たり設定したり出来るツールが存在する。

DevTerm/devterm-a06-gearbox at 0192f9dfa4500037ca52e0acf0b71aea1c580e8a · clockworkpi/DevTerm · GitHub

 

せっかくなのでCPUの動作状況を変更しつつ、ベンチマークを調べてみた!

このソフトはLinux環境でのパフォーマンスを計測するツールのようだ。良く使われているツールかどうかは分かっていないが、同じマシン同士の比較であれば問題ないだろう。

PassMark PerformanceTest Linux - Linux System Benchmark Software

 

まずは標準状態でのスコア。

f:id:PocketGriffon:20211207073916j:plain

[Number of Processes]が4(4コア)、そして大雑把に見るのであれば[CPU Mark]と[Memory Mark]の数値を見てみればよいだろう。細かい数値については興味があったら見てほしい。

標準状態ではCPU Markが333、Memory Markが405だ。

 

次にツールを使ってCPUの稼働状況をフルにしてみてからのベンチマーク

f:id:PocketGriffon:20211207074235j:plain

コア数が6となり、CPU Markが1105、Memory Markが851に跳ね上がった!

この状態であっても通常操作(shでコマンドを打つ、何かを動かす)で違いを感じる事はない。

 

そして次に最低速度に設定をしてみてのベンチマーク

f:id:PocketGriffon:20211207074714j:plain

コア数は1、CPU Markが45.4、Memory Markが196にまで下がった!

ここまで下がったとしても、通常の操作をしている時に違いは感じなかった。これはもうボトルネックがCPUの性能ではなく、SDカードなど別の要因があるからだろう。

 

せっかくなので、最高速の状態で今まで動かしてきたものを同時起動してみた!

f:id:PocketGriffon:20211207074958j:plain

PC-8801FM-7MSXPC-9801と4機種を同時に動かしてみたが、MSXの音楽がちょっと途切れるなぁ…という感じはあるものの、全部の機種がちゃんと動いた!(^^;;

 

ほとんどイジメのようなテストではあるけれども、メモリ的には余裕だろう!システムの安定性はさすがLinuxと言ったところか…(^-^)

さてさて、次は何をしてみようかね…(^m^)

 

ではまた次回!(^-^)/