- 目次ページに戻る / 前 / 次
- 前回からの差分をまとめて見る
前々回と前回でいろいろ片付いたので引数やります!
引数はスタックに置いて渡すそうです!
まず引数を1個だけ使うのをやってみます。
次のようにサブルーチン呼び出し(call
)の直前でスタックに push します。
(※間違いがあります。後述) サブルーチン呼び出し前: sp bp 2 3 引数を1個 push: push {引数} sp 1 {引数} bp 2 3 サブルーチン呼び出し: push bp sp 0 2 1 {引数} bp 2 3 サブルーチン呼び出し: cp sp bp sp bp 0 2 1 {引数} 2 3
で、サブルーチン内でこの引数にアクセスする場合は
bp
+ 1 の位置を見ればよい(※間違いがあります。後述)と。
ふむふむ。やってみましょう。
例として、サブルーチンの中で引数1 を reg_a
にセットするだけ、
というのをやってみます。
# 13_one_arg.vga.txt push 11 # 引数1 call sub exit label sub push bp cp sp bp # サブルーチンの処理本体 cp [bp+1] reg_a cp bp sp pop bp ret
いろいろ片付いたはずでしたが実際動かすとまだ足りないところがあります。 まず最初の push ができない。
vgvm.rb:182:in `block in start': Not yet implemented ("push") (11) (RuntimeError)
オペランドとして即値を受け取れていませんでした。対応させます。
--- a/vgvm.rb +++ b/vgvm.rb @@ -176,6 +176,8 @@ class Vm arg = @mem.main[@pc + 1] val_to_push = case arg + when Integer + arg when "bp" @bp else
次は……
vgvm.rb:216:in `copy': Not yet implemented ("copy src") ("[bp+1]") (RuntimeError)
うん、そんな気はしました。
cp
のコピー元のとこを修正します。
--- a/vgvm.rb +++ b/vgvm.rb @@ -212,6 +212,8 @@ class Vm @sp when "bp" @bp + when /^\[bp\+(\d+)\]$/ + @mem.stack[@bp + $1.to_i] else raise not_yet_impl("copy src", arg1) end
@bp + $1.to_i
を返すのではなく、
「スタックの @bp + $1.to_i
の位置にある値」を返したいので
@mem.stack[@bp + $1.to_i]
となります。
正規表現が若干ハードコーディング気味ですが動いたので気にせず進みます。 YAGNI です。
お次はこう:
vgvm.rb:227:in `copy': Not yet implemented ("copy dest") ("reg_a") (RuntimeError)
cp
のコピー先を修正!
--- a/vgvm.rb +++ b/vgvm.rb @@ -219,6 +219,8 @@ class Vm end case arg2 + when "reg_a" + @reg_a = src_val when "bp" @bp = src_val when "sp"
エラーが出なくなりましたが、動きがおかしいですね。
(終了時の状態) ================================ reg_a(4) reg_b(0) reg_c(0) zf(0) ---- memory (main) ---- 00 ["push", 11] 02 ["call", 7] pc => 04 ["exit"] 05 ["label", "sub"] 07 ["push", "bp"] 09 ["cp", "sp", "bp"] 12 ["cp", "[bp+1]", "reg_a"] 15 ["cp", "bp", "sp"] 18 ["pop", "bp"] 20 ["ret"] ---- memory (stack) ---- 0 3 1 4 sp => 2 11 bp => 3 0 exit
引数として指定した 11 が reg_a
にセットされてほしいのに、
4 がセットされています。
(しばし調べる)
分かりました。 サブルーチン呼び出しの際には引数だけじゃなくて 戻り先アドレスもスタックに積んでいたのでした。
0 3 1 4 ← 戻り先アドレス 2 11 ← 引数 3 0
なので、[bp+1]
じゃなくて [bp+2]
としないといけないのでした。
--- a/13_one_arg.vga.txt +++ b/13_one_arg.vga.txt @@ -7,7 +7,7 @@ label sub cp sp bp # サブルーチンの処理本体 - cp [bp+1] reg_a + cp [bp+2] reg_a cp bp sp pop bp
アセンブリコードを修正して動かすと、終了時の状態がこうなります。
================================ reg_a(11) reg_b(0) reg_c(0) zf(0) ---- memory (main) ---- 00 ["push", 11] 02 ["call", 7] pc => 04 ["exit"] 05 ["label", "sub"] 07 ["push", "bp"] 09 ["cp", "sp", "bp"] 12 ["cp", "[bp+2]", "reg_a"] 15 ["cp", "bp", "sp"] 18 ["pop", "bp"] 20 ["ret"] ---- memory (stack) ---- 0 3 1 4 sp => 2 11 bp => 3 0 exit
こんどは reg_a
にちゃんと 11 がセットされました!
されましたが!!
sp
と bp
の位置がずれてますね……。
これはずれていてはいけない
(サブルーチン呼び出し前と同じ状態になっていないといけない)はずです。
呼び出し規約は CDECL を参考にすることにしたので、ちょっと調べてみると、
CDECL では ret
で呼び出し元に戻った後に sp
の位置を戻すようで、
sp
に 1 を足すためにまた専用命令である add_sp
をシュッと追加します。
--- a/vgvm.rb +++ b/vgvm.rb @@ -152,6 +152,9 @@ class Vm when "add_ac" add_ac() @pc += pc_delta + when "add_sp" + @sp += @mem.main[@pc + 1] + @pc += pc_delta when "compare" compare() @pc += pc_delta @@ -234,7 +237,7 @@ class Vm case operator when "cp" 2 - when "set_reg_a", "set_reg_b", "label", "call", "push", "pop" + when "set_reg_a", "set_reg_b", "label", "call", "push", "pop", "add_sp" 1 when "ret", "exit" 0
アセンブリコードの方にも add_sp
を追加。
--- a/13_one_arg.vga.txt +++ b/13_one_arg.vga.txt @@ -1,5 +1,6 @@ push 11 # 引数1 call sub + add_sp 1 exit label sub
またまた動かすとこんどは……
================================ reg_a(11) reg_b(0) reg_c(0) zf(0) ---- memory (main) ---- 00 ["push", 11] 02 ["call", 9] 04 ["add_sp", 1] pc => 06 ["exit"] 07 ["label", "sub"] 09 ["push", "bp"] 11 ["cp", "sp", "bp"] 14 ["cp", "[bp+2]", "reg_a"] 17 ["cp", "bp", "sp"] 20 ["pop", "bp"] 22 ["ret"] ---- memory (stack) ---- 0 3 1 4 2 11 sp bp => 3 0 exit
sp が元の位置に戻りました! めでたしめでたし!!
修正後のアセンブリコードも改めて貼っておきます。
# 13_one_arg.vga.txt push 11 # 引数1 call sub add_sp 1 exit label sub push bp cp sp bp # サブルーチンの処理本体 cp [bp+2] reg_a cp bp sp pop bp ret