* kharc #01 -(by [[K]], 2025.05.30) ** (0) これはなに? -buntan-pcで遊んでいるうちに思い付いたもの。もはや言葉ではうまく説明できない。 -だから短期間で作って、こういうのあったらいいよね?と見せたい。 -なんて読めばいいの?「カーク」・・・ KH-Architecture が語源。 -仮想マシン(VM)なの?・・・そうとも言えるけど、そうではないともいえる。 -Cコンパイラなの?・・・ある意味ではそう。 -トランスパイラじゃないかな?・・・そうかもしれない。でもC言語をC言語にするんだけどね・・・。 -何ができるの?・・・CPUに依存しないデバッグ機能の提供。 -たぶんものができれば、「ああなるほどこういうことがやりたかったのね。じゃあ確かにこう作るしかなかったね」ってわかってもらえる。 ** 2025.05.30 Fri #0 -とりあえずの方針: --不便でも何でもいいので、とにかく動くものを作る。便利にする、きれいにするのは後回し。 --詳しく考えるのがめんどくさいので、x86っぽい構成にする。Rev2で真面目に設計すればいい。 -設計: --レジスタマシン。整数レジスタ4本(R)。アドレスレジスタ4本(A)。浮動小数点レジスタ4本(F)。コンパイラではレジスタ変数を考えないので、たぶんこれで足りる。 --なんか不安になってきたので、アドレスレジスタだけは8本にしよう。 --RxとAxはこのバージョンでは32bitということにする。 -まずはアセンブラを作る。 --MovRR(Rx,Rx); MovAA(Ax,Ax); MovFF(Fx,Fx); // レジスタ間転送 --MovRI(Rx,imm32); --AddRI(Rx,imm32); SubRI(Rx,imm32); -関数呼び出し規則としては、基本的にどのレジスタも破壊可能だけど、スタックポインタであるA0とグローバルベースのA1は破壊しない。 -ただ123を返すだけの関数。 MovRI(R0,123); Ret(); -1から10までの和を計算する関数。 MovRI(R0,0); MovRI(R1,1); LbI(0); AddRR(R0,R1); AddRI(R1,1); CmpJneRII(R1,11,0); Ret(); // == JmpA(A2); -再帰でフィボナッチ数を計算する関数。スタックトップに引数がある。 LbI(0); LodRMd(R0,A0,0); // R0=[A0+0]; CmpJgRII(R0,1,1); // if (R0>1) goto 1; Ret(); LbI(1); SubAI(A0,16); StoAMd(A2,A0,8); // [A0+8]=A2; SubRI(R0,1); StoRMd(R0,A0,0); CalII(0,2); // == MovAI(A2,2); JmpI(0); LbI(2); StoRMd(R0,A0,12); LodRMd(R0,A0,16); SubRI(R0,2); StoRMd(R0,A0,0); CalII(0,3); LodRMd(R1,SP,12); AddRR(R0,R1); LodAMd(A2,A0,8); AddAI(A0,16); Ret(); -kharcのアセンブラは、機械語ではなくC言語のソースコードを出力する。 ** 2025.06.02 Mon #0 -もしかしたら説明できるかもしれないので、ちょっと説明してみます。 -[1]何かプログラムを書いたとして、そのプログラムがある関数f()を何回呼んでいるか知りたくなったとします(たとえばデバッグ中だと考えてください)。その場合、関数f()の中にprintf()を仕込めば、関数が呼ばれたログを簡単に作ることができます。 --俗にいうprintfデバッグです。これのいいところは、ある条件を満たしているときにf()を呼んだケースのみを検出したいとか、そういう複雑な条件をいくらでも記述できるところです。 -ではこのプログラムがどういうタイミングでメモリアクセスしているか、それを全部追跡したくなったとしましょう。そうすれば「なんで今このオブジェクトが壊れているのか。壊したやつは誰だ?!」みたいなバグもすぐに追跡できます。 -でもそこで気づくのです。メモリアクセスなんてコード内にありふれていて、とてもじゃないけど簡単には追跡できないな、と。 -ほかにもいろいろありえます。すべての加算命令が怪しくて意図しないオーバーフローを検出したいけど、検出できないので探しようがない、とか・・・。 -[2]こういうとき、私には必殺技があります。それはエミュレータを使うことです。エミュレータなら、すべてのメモリアクセスを監視するとか、加算命令を監視するとか、そんなの余裕なのです。 -なぜエミュレータが必殺技なのかと言えば、私はもともとエミュレータが大好きで、エミュレータがすべての問題を解決するのだ、がははは!みたいなことをいつも言っているような人間だったからです。 -エミュレータ教の信者、いや教祖かもしれません。大学生の時には実際にエミュレータを一つ書いたこともありました(事実上のデビュー作です)。 -[3]ということで、x86エミュレータを適当に入手して改造すればいいわけですが、既存のx86エミュレータはどれもかなり巨大です。エミュレーション速度を上げるためにたくさんの工夫が入っていて、複雑になっています。というかそもそもx86の命令体系が複雑なんです。 -私はただデバッグ支援のために気軽に改造したいだけなのに、やらなきゃいけないことが多くて、「うーんそれだったらエミュレータを使わない方法でデバッグしようかな」と心が折れてしまいます。 -[4]そうやって何回も心を折ってきたわけですが、さすがにこの繰り返しはアホだと自覚しました。シンプルで改造しやすいエミュレータを作ればいいのです。エミュレータ教の信者なのだからそれくらいできないでどうしますか。 -もちろんx86やARMなどとの互換性はないです。エミュレータが作りやすいようなシンプルなやつにします。 -名前がないと説明しにくいので、これをkharcアーキテクチャと呼ぶことにします(kharcの語源の中にアーキテクチャがあるので、アーキテクチャが2個になっているところがダサいけど)。 -アセンブラしかないっていうのは不便すぎるので、kharc用のCコンパイラも作ります。 -[5]普通にエミュレータを作ると大変だし、その割に速度が出ないので、kharcのアセンブラからC言語に変換するツールを作ります。そのC言語をgccやVCなどの普通のCコンパイラにかけると、実行ファイルができて実行できます。 -Cコンパイラは最適化をやってくれるので、この方法ならそこそこの速度が出るかもしれません。 -アセンブラからC言語に変換の際には変換ルールみたいなものを決めておくわけですが、そこを改造することで、すべてのメモリアクセスを監視するとか、すべての加算命令を監視するとか、何でもできるようになります。 ** 2025.06.02 Mon #1 -整数レジスタ R0~R7 : R7はデバッグ用のレジスタで、今ソースコードのどこを実行中なのか記憶するためのもの(例えば行番号が入るイメージ)。リリース時はこのレジスタは使わない。 -アドレスレジスタ A0~A7 : A7はPC, A6はSP, A5はリターンアドレス(5/30版とは違っている) -浮動小数点レジスタ F0~F7 : 特に規定なし ** 2025.06.04 Wed #0 -kharc向けにCコンパイラを書いていますが、私はこの手段がすごく正しいと感じます。 -つまり、CコンパイラはいきなりそれぞれのCPUに向けたアセンブラを出力するのではなく、もっと平易で共通化された中間言語を出力して、それを各アーキテクチャにコンパイルされるべきなのです。 -ってそれがLLVM-IRってことか。うーん、それにしてはLLVMは複雑な気がするけど、まあ最適化のための情報を維持するためにはしょうがなかったのかなあ? ** 2025.06.04 Wed #1 -完全なCコンパイラを作るのではなく、簡易な(=手抜き満載の)Cコンパイラを作ろう。まじめに作るのは別に将来のバージョンでもいいと思うので。 -今回の開発ポリシーは [[a25_kharcs1]] みたいに、手早く雑に作ること。 -ちゃんとしたものは誰でも作れる。「簡単に作れるのにそこそこ使える」ものは、センスのある人にしか作れない(私にそのセンスがあるかどうかは、まだわからないけど)。そして私が欲しいのは「簡単に作れるのにそこそこ使える」もののほうなのだー。 ** 2025.06.04 Wed #2 -Cコンパイラを強引に作ればまあ作れそうだけど、できれば楽に作りたい。どういう仕組みにすれば楽に作れるか。そういうことに無駄にこだわりたくなるので、どうしても余計に時間がかかる・・・。 ** 2025.06.06 Fri #0 -kharc用のCコンパイラを、とりあえず「kcc」と呼ぶことにします。 -kccは関数が宣言できて、変数宣言もできて、インラインアセンブラも使えるというところまでは来ました。 -kccの中では番号じゃなくて名前でラベルも使えます。 -ということで、現状では高級なアセンブラでしかないけど、今までと比べたら格段に便利です! -月曜になったら、フィボナッチ数計算プログラムをkccのインラインアセンブラで書いてみよう。きっと前よりは書きやすいはずです。 ** 2025.06.08 Sun #0 -[2025.04.11 金] buntan-pc用のアセンブラkuasを作り始める。 -[2025.04.16 水] kuasの主要な部分が完成。 -[2025.04.25 金] kuasがほぼ完成。 -[2025.04.25 金] MS-DOS用のアセンブラをC言語に移植する方法についていろいろと実験をしてみる(これがのちのkharcに活かされる)。 -[2025.05.07 水] C言語のプリプロセッサの自作を始める。 -[2025.05.23 金] プリプロセッサが完成。kcpp0.c:371行(12.2KB)。acl3.c:498行(14.9KB)。 -[2025.05.30 金] kharcの開発が始まる。まずはVM仕様の検討と、高速な実行環境の構築。 -[2025.06.04 水] kharc用のCコンパイラの開発が始まる。 -[Q]なんか最近開発が速くないですか? -[A]速いと思います。まあ今までが遅かったということもあるんですが(苦笑)、kuasを作る時にacl3ライブラリを作るようにして、これで私の開発力が底上げされました。 -自分が作ったもので自分の開発力を上げる。作れば作るほど自分の開発力がupしていくという構想を数年前から考えていましたが、やっと実になってきたのかもしれません。 -また私は今まで何度も言語を作ってきたということもあって、やっとコツが分かってきたというのもあると思います。 -それと、私はそもそもそんなにたくさん書いてないのです。だから速くできるのは当然でもあるのです。 ** 2025.06.13 Fri #0 -今の野心としては、kharcバイナリをいろんなアーキテクチャに変換する仕組みが欲しい。そうすれば私はkharc用に言語を作るだけで、いろんなアーキテクチャに対応したことになる。 -もう一つの野心として、x86とかx64とかARMとかAArch64とかRISC-Vとかをkharcに変換する仕組みも欲しい。そうすれば、kharcの実行環境さえ作れば、全部に対応できることになる。 -エミュレータOSの野望が再開する?? ** 2025.06.22 Sun #0 -kccでうまくやれば、persistent-Cと同じようなことができそうな気がしてきた。 -あとacl3ライブラリにtek5用の関数を付けたら、OSASKアプリみたいなやりかたでプログラミングできて超楽しいかもしれない。 -今のkharcバイナリをhh4でエンコードしたら、それってOSECPU-VMっぽい? -こうして私の今までのいろいろがkccに統合されたら、なんかすごいことになる?? ** 2025.06.24 Tue #0 -kharcはbuntan-pcのようなスタックマシン向けのアセンブラにも効率よく変換できたのだから、最適化が効きやすそうなC言語形式にだって変換できるはずだ。 -kharcはbuntan-pcのようなスタックマシン向けのアセンブラにも効率よく変換できたのだから、最適化が効きやすそうなC言語形式にだって変換できるはずだ(少なくとも理想はそうあるべきだ)。 -そのためには、できるだけ単純変数(配列でもなく構造体でもない)を使う形に変換できればいいのだと思う。 -これがうまくできたら、kccで書いて、gccで最適化する方法で、実行速度に妥協する必要がなくなる。 -これがうまくできたら、kccで書いてgccで最適化する方法で、実行速度に妥協する必要がなくなる。 -普段の開発時はkccで実行してデバッグして、スピードが欲しいときにgccも併用するイメージ。