TLDR; 私はちっぽけな RISC-V エミュレーターを C から zig に移植しました。それは (平和的に) Linux を起動します
https://github.com/ringtailsoftware/zig-minirv32
Zig は、既存の C コードを段階的に移植するのに理想的な言語です。
(WASMモデル:
https ://ringtailsoftware.github.io/)
連絡先:
Zig?
Zig は非常にユニークなシステム プログラミング言語です。
オンラインページには、
Zig は、かなりの、最適で再利用可能なアプリケーションを維持するための独自計画のプログラミング言語およびツールチェーンではなくなりました。
組み込みシステムおよびネットワーキング担当者としての私の観点からすると、Zig は「より優れた C 」。 これにより、まったく同じ低レベルのゲイン エントリが許可されますが、より優れた言語構成により、多くのクラスの一般的なエラーが取り除かれます。 必然的に、人々は同じ住居をかなり計画しているので、Zig to Rust を検討するでしょう。 私の旅は、Zig は教えられて苦労するのがかなり単純であり、Zig の最小限の構文は、時にはより冗長なコードを書くことを意味する可能性があるかもしれませんが、そのコードは簡単に教えられます-私は贅沢です.
言語の感触を得るために見ていても、 Ziglingsは充実の紹介です Mini-RV32ima
Mini-RV32ima は単一の C ヘッダ ファイルの RISC-V エミュレータです。 オンラインページ
から:
RISC-V rv32ima/ を実装します。 Zifencei†+Zicsr (および部分的な su)、CLINT および MMIO を使用 正確なコードの400系統を用意しています
依存関係がなく、もはや libc もありません*) 免責事項: 私は RISC-V についてほとんど何も知りません。 C コードを Zig に移植する戦略は、RISC-V リファレンス マニュアルに対応する必要があるため、行ごとに翻訳することになりました。
Zigの秘密兵器
Zigコンパイラ
ジグ
、さらに C コンパイラです - それは本物ですclang。 これにより、C と Zig を組み合わせたイニシアチブを簡単に考案できます。私の移植の最初のモデルは、これで少し贅沢に見えました、Zigは次のように呼びかけます
doEverything() として知られる C 機能本物の中古 C
ファンダメンタルと呼ぶ()( に名前を変更) _mainシンボル名の衝突を判断して操縦する) .Zig:
const rv32=@cImport({ @cInclude("ミニ-rv32ima.h"); } pub fn 基本() !void { rv32.doEverything(); }
C:
void doEverything(void) { char *argv={ "minirv32", "-f", "イメージ" }; _main(3, argv); } int _main(int argc, char *argv) { ... }
Zig は Zig を発明マシンとして使用します (もう
construct/ cmake/autotools /その他多数) および
invent.zig コードに C を追加するようにすると、このように贅沢な気分になります:const rv32ima_flags=[_] const u8{ "-壁", }; const rv32ima_sources=[_] const u8{ "mini-rv32ima/mini-rv32ima.c", }; fn createrv32ima(b: *std.invent.Builder) *std.invent.LibExeObjStep { const lib=b.addStaticLibrary("rv32ima", null); lib.addCSourceFiles(&rv32ima_sources, &rv32ima_flags); lib.addIncludePath("mini-rv32ima"); lib.linkSystemLibraryName("c"); ライブラリを返します。 }
実際のスリザー
zig invent。 それを含まなかった場合動作する実行ファイルを購入しました。 すべて C で書かれていますが、Zig でコンパイルされています。 これは無意味に聞こえますが、一方で、Zig は非常に優雅な堕落したコンパイラ
libc 依存性がある場合は、WASM で贅沢な自立型プラットフォームを追加で発明する可能性がありますが、それについては後で詳しく説明します.
一度に魅力的な機能
Mini-rv32 はいくつかの C 関数を使用して、シミュレートされたハードウェアへのメモリ書き込みを処理します。それらを Zig に切り替えましょう。
HandleControlStore
は、エミュレータがコードを実行して、メモリにマップされた IO 計測器に知識を保存しようとするケースを扱います。
0x10000000にバイトを書き込む 端末から出力します.C:
uint32_t HandleControlStore( uint32_t addy, uint32_t val ) { if( addy==0x10000000 ) { //UART 8250 / 16550 Recordsdata Buffer printf( "%c", val ); fflush(標準出力); 0を返します。 }
Zig:
export fn HandleControlStore(addr:u32, val:u32) callconv(.C) u32 { if (addr==0x10000000 ) { //UART 8250 / 16550 Recordsdata Buffer std.debug.print("{c}", .{@intCast(u8, val)}); 0を返します。 }
Zig では、本物を追加します
export
およびcallconv(.C)エクスポートしましたC から呼び出し可能な機能です。C の_main() を呼び出す Zig を購入しました。 はエミュレータコアを実行しますが、IO を構成するために Zig を呼び出します(
std.debug から出力されています) とりあえずここは絶対に失敗しないから)
エミュレータのコアは、単一のアキュムレート - デコード - 実行サイクルを実行する機能です。 Cでは、これで贅沢をしているように見えます:
int32_t MiniRV32IMAStep( struct MiniRV32IMAState 発音、uint8_t 画像、uint32_t vProcAddress、uint32_t 経過時間、int カウント, uint32_t ramSize);と発音する は主に最新のレジスタとフラグです (実際には RAM の最後に保持されます)、画像はマシンのメモリであり、測定値ですramSize.
_main()Cガジェットの問題次に、ステップ機能をループで呼び出します。 次に、心の中身を発音して Zig に切り替えます:メモリを割り当てます:
const メモリ=std.heap.page_allocator.alignedAlloc(u8, 4, ramSize); を目指してください。
RAM の結論の構造体。 Zig の構造体を再定義する必要がなかったことを隠してください - C ヘッダー ファイルを教えて、すべての部分を正確にマップする必要があります (はい、Zig は、C がスライスを起動し、アライメントを知るためにできるポインター演算の合計を制定できます) :レベルa
MiniRV32IMAStatevar 発音:*rv32.MiniRV32IMAState=@ptrCast(*rv32.MiniRV32IMAState, memory.ptr + (ramSize - @sizeOf(rv32.MiniRV32IMAState)) );
swap ケース。それから、Zig は本物のステップ機能を連続して呼び出すことができます。 非表示を表示します。フォーム マッピングは必要ありません。
const ret=rv32.MiniRV32IMAStep(コア、memory.ptr、0、経過時間、カウント、ramSize);エミュレータコアの書き換え
エミュレータの心臓部は MiniRV32IMAStep()
. CPUエミュレーターから期待されるように、それは適切なswap
総操作を強制するアサーション。 Zigに移植する際の細かな調整方法を紹介します。
C:
swap( ir & 0x7f ) { case 0b0110111: // LUI rval=( ir & 0xfffff000 ); スマッシュ; case 0b0010111: // AUIPC rval=非公開のコンピューター + ( ir & 0xfffff000 ); スマッシュ; case 0b1101111: { // JAL int32_t reladdy=((ir & 0x80000000)>>11) | ((ir & 0x7fe00000)>>20) | ((ir & 0x00100000)>>9) | ((ir&0x000ff000)); if( reladdy & 0x00100000 ) reladdy |=(int32_t)0xffe00000; // シグナル拡張。 rval=非公開のコンピューター + 4; 非公共のコンピューター=非公共のコンピューター + (uint32_t)(reladdy - 4); スマッシュ; } case 0b1100111: { // JALR uint32_t imm=ir>> 20; int32_t imm_se=imm | (( imm & 0x800 )?0xfffff000:0); rval=非公開のコンピューター + 4; 非公共のコンピュータ=( (REG( (ir>> 15) & 0x1f ) + imm_se) & ~1) - 4; スマッシュ; } ...Zig:
スワップ (ir & 0x7f) { 0b0110111=> rval=(ir & 0xfffff000), // LUI 0b0010111=> rval=非公開コンピュータ +% (ir & 0xffffff000), // AUIPC 0b1101111=> { // JAL var reladdy: i32=@intCast(i32, ( (ir & 0x80000000)>> 11) | ((ir & 0x7fe00000)>> 20) | ((ir & 0x00100000)>> 9) | ((ir & 0x000ff000))); if ((reladdy & 0x00100000) !=0) { reladdy=@bitCast(i32, @bitCast(u32, reladdy) | 0xffe00000); // シグナル拡張。 rval=非公開のコンピューター + 4; 非公開コンピュータ=非公開コンピュータ +% @bitCast(u32, reladdy - 4); }, 0b1100111=> { // JALR const imm: u32=ir>> 20; var imm_se: i32=@intCast(i32, imm); if (imm & 0x800 !=0) { imm_se=@bitCast(i32, imm | 0xfffff000); rval=非公開のコンピューター + 4; const newpc: u32=((pronounce.regs[(ir>> 15) & 0x1f] +% @bitCast(u32, imm_se)) & ~@as(u32, 1)) - 4; 非公共のコンピュータ=newpc; },
刻印すべきいくつかの問題:
Zig に
スマッシュ
アサーション、ループから抜け出すことはなくなり、もはや合計スマッシュ.Zig は C で贅沢な三項演算子を含んでいません。
a ? b : c
は long make asif (a) b else c 。 もっと冗長だとしても、これはより読みやすい計画であると結論付けます。 rval=非公開のコンピュータ + ( ir & 0xfffff000 )
ひょっとしたら平和になるかもしれません。 Zig では、. Zig では、整数のオーバーフロー (一部の発明モードで) により実行時エラーが発生します。 したがって、rval=非公開のコンピューター +% ( ir & 0xfffff000)
.JAL
の最後に、C モデルは を使用して署名されたアドレスから PC を更新します。 非公開コンピュータ=非公開コンピュータ + (uint32_t)(reladdy - 4). Zig では、生ビットをu32
として定義するためにコンパイラにため息をつくことができます。 (およびオーバーフローをラップ) を使用) 非公開コンピュータ=非公開コンピュータ +% @bitCast(u32, reladdy - 4).
C では、コードは
& ~1、それはどのような形を識別するために左側の面を見て構成しています1& ~@as(u32, 1) と発音する必要があります。 .WASMZigはWASMをターゲットとしてサポートしています. エミュレーターを完全に Zig に移植するよりも早く、C コード モデルを WASM に発明し、ブラウザーで実行することができます。 これを実行するには、すべて
libcに依存します。 を削除したい - IO 処理関数を Zig.C で作業するのは簡単で、JavaScript を介してアクセスする機能をエクスポートするのも簡単なので事実です
Zig:
![]()
export fn tty_read(b:u8) void { // IO を処理します }
JavaScript:
おわりにreturn WebAssembly.instantiate(bytes, imports).then((outcomes)=> { var tty_read=results.occasion.exports.tty_read; ... });
xterm.js 。 確かに、JSLinux の方が優れていましたが、逆に平和でリラックスできるハックです…一時的な思考の証明として、WASM にコンパイルされた mini-rv32 の C モデルを使用してブラウザーのデモをハックし、
https:// ringtailsoftware.github.io/
純粋にジグを書いて遊んでいます。 ほんの少しのイニシアチブの後、私は現在、ほとんどの構文を知っているという点に達しており、ライブラリ関数が必要になった後に贅沢に見えるものについて、まともな賭けをする立場にいます。 長時間の C プログラマーとして、Zig は私が仲介する能力に適しています。 私はそれが興味深い未来を買ったと思います. https://www.hodgepig.org/zero/ (Zig のマスコットが登場するスポーツで、WebGL 強化用にゼロ グラフィックス ライブラリを使用)
https://github.com/ringtailsoftware/zig-embshell (テキストコマンドを実行するためのちっぽけな REPL/シェル)
https://github.com/ringtailsoftware/zig-embeddir (多様なレコードデータからの知識をバイナリに埋め込むための、Zig を使用した集合時間のトリック)