lbstk00の説明
lbstk00とは?
- 簡単に言うと、OSECPUのアセンブラを支援するためのツールで、これがあると煩雑なラベル管理を肩代わりしてくれます。
- x86などの一般的なCPUの開発ツールでいうと、リンカに相当すると思われます。
- 語源はlabel-stackです。
- C言語の標準ライブラリだけで書いた場合は150行くらいの非常に簡単なものです。できればOSECPUに移植したいと思っています。
- 最初のバージョンはosecpu010aにバンドルされています。
きっかけ
- OSECPUでは関数を呼ぶときに関数呼び出しの命令があるわけではなく、ただ目的の関数へJMPしています。しかしそれでは当然戻ってこられません。ということで、JMPの直前に、P1Eレジスタに「終わったらここへJMPしてください」というアドレスを格納する命令を置きます。
PLIMM(P1E, 4); JMP(12); LB0(4);
- この例では、LB0(12);の関数を呼んでいます。
- もしこの関数を連続で2度呼ぶとしたら、こうなります。
PLIMM(P1E, 4); JMP(12); LB0(4);
PLIMM(P1E, 5); JMP(12); LB0(5);
- つまり関数呼び出しのたびにラベルを作る必要があって、しかもその番号は重複してはいけないのです。このラベル番号の管理はプログラムが長くなってくると非常に面倒になってきます。
- このような仕様なのには理由があります。まずOSECPUはラベルのあるところ以外には絶対に分岐しないのです。どこでも好きなところに分岐できてしまう普通のCPUとは違います。好きなところに分岐できてしまうことはセキュリティ的に危険ですし(だからx86にはコールゲートなどの仕組みを使って、分岐先を制限する仕組みを持っているわけです)、プログラム上のどこで他から処理が合流するか事前に分かるこの仕様はJITコンパイラにとっては有利です。
- それならosecpu.exeがもっとがんばってうまく処理すればいいではないかという指摘もあるかと思います。しかしそれはosecpu.exeが複雑になってしまいます。osecpu.exeは移植しやすい規模を維持するべきです。・・・とまあそんなわけで、この問題を解決するためのツールをosecpu.exeの外に作ることになりました。
仕組み
- 上記の関数呼び出しを次のように書くことにしました。
lbstk1(2); PLIMM(P1E, lbstk2()); JMP(12); LB0(lbstk2());
- これは次のような意味を持ちます。
- lbstk1(2); : 新規にラベル用の番号を一つ発行し、その番号を2回スタックにつみます。
- 2回つむのは、このラベル番号を2回参照するからです。
- lbstk2() : スタックからラベル番号を一つ取ってきます。
- このような機構により、以下のように何度書いても問題なくなりました。
lbstk1(2); PLIMM(P1E, lbstk2()); JMP(12); LB0(lbstk2());
lbstk1(2); PLIMM(P1E, lbstk2()); JMP(12); LB0(lbstk2());
- このスタック操作は、ソースの書き換えの際にラベル番号を求めるために仮想的に行うものであって、アプリの実行時に行うわけではありません。
応用
- osecpu_asm.hには以下のような記述があります。
#define LOOPSTART(n) lbstk1(n); LB0(lbstk2())
#define CONTINUE lbstk2()
- これがあると以下のような表記が可能になります。
#define sum R00
#define i R01
#define const1 R30
#define const10001 R31
LIMM(const1, 1);
LIMM(const10001, 10001);
LIMM(i, 0);
CP(sum, i);
LOOPSTART(2); /* CONTINUEを使う回数+1を書く */
ADD2(sum, i);
ADD2(i, const1);
CMPJNE(i, const10001, CONTINUE);
- このようにループを書く際にラベル番号を気にしないで済みます。またこのループは入れ子にしていても問題なく機能します。
発展
こめんと欄
- このページにこめんと欄はありません。このページの内容にコメントしたいときはimpressionsにお願いします。