vm2gol v2 製作メモ(33) 座標の補正 (2)



前回の座標補正の続き……の前に、 先に引っかかるところを潰しておきます。

codegen_return() でローカル変数を返そうとすると 参照の解決ができずにエラーになるので、修正。

--- a/vgcg.rb
+++ b/vgcg.rb
@@ -307,8 +307,8 @@ def codegen_return(lvar_names, stmt_rest)
   when Integer
     alines << "  set_reg_a #{retval}"
   when String
-    case retval
-    when /^vram\[([a-z0-9_]+)\]$/
+    case
+    when /^vram\[([a-z0-9_]+)\]$/ =~ retval
       var_name = $1
       case
       when lvar_names.include?(var_name)
@@ -317,6 +317,9 @@ def codegen_return(lvar_names, stmt_rest)
       else
         raise not_yet_impl("retval", retval)
       end
+    when lvar_names.include?(retval)
+      lvar_addr = to_lvar_addr(lvar_names, retval)
+      alines << "  cp #{lvar_addr} reg_a"
     else
       raise not_yet_impl("retval", retval)
     end

はい、では本題に戻りましょう。

前回は左端を超えてしまった場合の処理を作りました。

今回は同じように、右端・上端・下端を超えてしまった場合の処理を追加します。

前回の vgtコードをコピーして、

cp 32_adjust_index.vgt.json 33_adjust_index_2.vgt.json

コメントをちょっと修正して、

--- a/33_adjust_index_2.vgt.json
+++ b/33_adjust_index_2.vgt.json
@@ -39,14 +39,14 @@
     , ["set", "yt", ["+", "y", -1]]
     , ["set", "yb", ["+", "y",  1]]
 
-    , ["_cmt", "★ 補正の直前"]
+    , ["_cmt", "★ xl の補正の直前"]
     , ["case"
       , [["eq", "xl", -1]
-        , ["_cmt", "★ -1 だった場合"]
+        , ["_cmt", "★ 左端を超えた場合"]
         , ["set", "xl", ["+", "w", -1]]
         ]
       ]
-    , ["_cmt", "★ 補正の直後"]
+    , ["_cmt", "★ xl の補正の直後"]
 
     , ["var", "tmp"]

まず右端から。

--- a/33_adjust_index_2.vgt.json
+++ b/33_adjust_index_2.vgt.json
@@ -48,6 +48,15 @@
       ]
     , ["_cmt", "★ xl の補正の直後"]
 
+    , ["_cmt", "★ xr の補正の直前"]
+    , ["case"
+      , [["eq", "xr", "w"]
+        , ["_cmt", "★ 右端を超えた場合"]
+        , ["set", "xr", 0]
+        ]
+      ]
+    , ["_cmt", "★ xr の補正の直後"]
+
     , ["var", "tmp"]
 
     , ["_cmt", "左上"]
@@ -99,7 +108,7 @@
     , ["var", "y"]
     , ["set", "y", 0]
 
-    , ["call", "count_alive", "w", 0, 2]
+    , ["call", "count_alive", "w", 4, 2]
     ]
   ]

動作の確認のため、注目するセルを (4, 2) として、 xr == 5 だったら xr = 0 に補正されることを期待して、実行。

================================
52: reg_a(0) reg_b(1) reg_c(0) zf(0)
---- memory (main) ----
      142   ["set_reg_a", 1]
      144 ["label", "end_eq_2"]
      146   ["set_reg_b", 1]
      148   ["compare"]
      149   ["jump_eq", 155]
      151   ["jump", 169]
      153 ["label", "when_1_0"]
      155   ["_cmt", "★~左端を超えた場合"]
      157   ["set_reg_a", "[bp+2]"]
      159   ["set_reg_b", -1]
      161   ["add_ab"]
      162   ["cp", "reg_a", "[bp-2]"]
      165   ["jump", 169]
      167 ["label", "end_case_1"]
      169   ["_cmt", "★~xl~の補正の直後"]
pc => 171   ["_cmt", "★~xr~の補正の直前"]
      173   ["set_reg_a", "[bp-3]"]
      175   ["set_reg_b", "[bp+2]"]
      177   ["compare"]
      178   ["jump_eq", 186]
      180   ["set_reg_a", 0]
      182   ["jump", 190]
      184 ["label", "then_4"]
      186   ["set_reg_a", 1]
      188 ["label", "end_eq_4"]
      190   ["set_reg_b", 1]
      192   ["compare"]
      193   ["jump_eq", 199]
      195   ["jump", 208]
      197 ["label", "when_3_0"]
      199   ["_cmt", "★~右端を超えた場合"]
      201   ["cp", 0, "[bp-3]"]
---- memory (stack) ----
         25 0
         26 0
         27 0
         28 0
         29 0
         30 0
         31 0
         32 0
sp    => 33 3
         34 1
         35 5 ... xr
         36 3
         37 0
   bp => 38 47
         39 457
         40 5
         41 4

補正前に 5 になっていた xr が……

(step=62)

      171   ["_cmt", "★~xr~の補正の直前"]
      173   ["set_reg_a", "[bp-3]"]
      175   ["set_reg_b", "[bp+2]"]
      177   ["compare"]
      178   ["jump_eq", 186]
      180   ["set_reg_a", 0]
      182   ["jump", 190]
      184 ["label", "then_4"]
      186   ["set_reg_a", 1]
      188 ["label", "end_eq_4"]
      190   ["set_reg_b", 1]
      192   ["compare"]
      193   ["jump_eq", 199]
      195   ["jump", 208]
      197 ["label", "when_3_0"]
pc => 199   ["_cmt", "★~右端を超えた場合"]
      201   ["cp", 0, "[bp-3]"]
      204   ["jump", 208]
      206 ["label", "end_case_3"]
      208   ["_cmt", "★~xr~の補正の直後"]

「右端を超えた場合」の分岐に入って

================================
65: reg_a(1) reg_b(1) reg_c(0) zf(1)
---- memory (main) ----
      178   ["jump_eq", 186]
      180   ["set_reg_a", 0]
      182   ["jump", 190]
      184 ["label", "then_4"]
      186   ["set_reg_a", 1]
      188 ["label", "end_eq_4"]
      190   ["set_reg_b", 1]
      192   ["compare"]
      193   ["jump_eq", 199]
      195   ["jump", 208]
      197 ["label", "when_3_0"]
      199   ["_cmt", "★~右端を超えた場合"]
      201   ["cp", 0, "[bp-3]"]
      204   ["jump", 208]
      206 ["label", "end_case_3"]
pc => 208   ["_cmt", "★~xr~の補正の直後"]
      210   ["sub_sp", 1]
      212   ["_cmt", "左上"]
      214   ["push", "[bp-4]"]
      216   ["push", "[bp-2]"]
      218   ["push", "[bp+2]"]
      220   ["_cmt", "call_set~~vram_get"]
      222   ["call", 41]
      224   ["add_sp", 3]
      226   ["cp", "reg_a", "[bp-6]"]
      229   ["set_reg_a", "[bp-1]"]
      231   ["set_reg_b", "[bp-6]"]
      233   ["add_ab"]
      234   ["cp", "reg_a", "[bp-1]"]
      237   ["_cmt", "上"]
---- memory (stack) ----
         25 0
         26 0
         27 0
         28 0
         29 0
         30 0
         31 0
         32 0
sp    => 33 3
         34 1
         35 0 ... xr
         36 3
         37 0
   bp => 38 47
         39 457
         40 5
         41 4

0 になりました! いいですね。


同様に、上端を超えた場合の補正を追加:

--- a/33_adjust_index_2.vgt.json
+++ b/33_adjust_index_2.vgt.json
@@ -24,7 +24,7 @@
     ]
   ]
 
-, ["func", "count_alive", ["w", "x", "y"]
+, ["func", "count_alive", ["w", "h", "x", "y"]
   , [
       ["var", "count"]
     , ["set", "count", 0]
@@ -57,6 +57,15 @@
       ]
     , ["_cmt", "★ xr の補正の直後"]
 
+    , ["_cmt", "★ yt の補正の直前"]
+    , ["case"
+      , [["eq", "yt", -1]
+        , ["_cmt", "★ 上端を超えた場合"]
+        , ["set", "yt", ["+", "h", -1]]
+        ]
+      ]
+    , ["_cmt", "★ yt の補正の直後"]
+
     , ["var", "tmp"]
 
     , ["_cmt", "左上"]
@@ -108,7 +117,7 @@
     , ["var", "y"]
     , ["set", "y", 0]
 
-    , ["call", "count_alive", "w", 4, 2]
+    , ["call", "count_alive", "w", "h", 2, 0]
     ]
   ]

上端・下端の場合は幅の代わりに高さ h が必要なので count_alive() の引数として渡すようにしました。

右端、左端のときと同じ要領なので動作確認の結果は省略します。

最後に下端。

--- a/33_adjust_index_2.vgt.json
+++ b/33_adjust_index_2.vgt.json
@@ -66,6 +66,15 @@
       ]
     , ["_cmt", "★ yt の補正の直後"]
 
+    , ["_cmt", "★ yb の補正の直前"]
+    , ["case"
+      , [["eq", "yb", "h"]
+        , ["_cmt", "★ 下端を超えた場合"]
+        , ["set", "yb", 0]
+        ]
+      ]
+    , ["_cmt", "★ yb の補正の直後"]
+
     , ["var", "tmp"]
 
     , ["_cmt", "左上"]
@@ -117,7 +126,7 @@
     , ["var", "y"]
     , ["set", "y", 0]
 
-    , ["call", "count_alive", "w", "h", 2, 0]
+    , ["call", "count_alive", "w", "h", 2, 4]
     ]
   ]

四隅の各セルに注目している場合も確認しておきましょう。

左上 (0, 0) に注目している場合:

================================
98: reg_a(0) reg_b(1) reg_c(0) zf(0)
---- memory (main) ----
(略)
      284   ["cp", 0, "[bp-5]"]
      287   ["jump", 291]
      289 ["label", "end_case_7"]
pc => 291   ["_cmt", "★~yb~の補正の直後"]
      293   ["sub_sp", 1]
      295   ["_cmt", "左上"]
      297   ["push", "[bp-4]"]
(略)
---- memory (stack) ----
         24 0
         25 0
         26 0
         27 0
         28 0
         29 0
         30 0
         31 0
sp    => 32 1 ... yb: 0 + 1            
         33 4 ... yt: 0 - 1 => 4 に補正
         34 1 ... xr: 0 + 1            
         35 4 ... xl: 0 - 1 => 4 に補正
         36 0
   bp => 37 47
         38 542
         39 5
         40 5

期待どおりの動きです。

右上 (4, 0) に注目している場合 (以下、同様なので yb の補正の直後の時点でのスタック領域の一部だけ示します):

(step=95)

sp    => 32 1 ... yb: 0 + 1
         33 4 ... yt: 0 - 1 => 4 に補正
         34 0 ... xr: 4 + 1 => 0 に補正
         35 3 ... xl: 4 - 1
         36 0
   bp => 37 47

左下 (0, 4) に注目している場合:

(step=95)

sp    => 32 0 ... yb: 4 + 1 => 0 に補正
         33 3 ... yt: 4 - 1
         34 1 ... xr: 0 + 1
         35 4 ... xl: 0 - 1 => 4 に補正
         36 0
   bp => 37 47

右下 (4, 4) に注目している場合:

(step=92)

sp    => 32 0 ... yb: 4 + 1 => 0 に補正
         33 3 ... yt: 4 - 1
         34 0 ... xr: 4 + 1 => 0 に補正
         35 3 ... xl: 4 - 1
         36 0
   bp => 37 47

(2, 2) に注目している場合(補正が発生しない場合)も念のため再度確認しておきましょうか。

(step=88)

sp    => 32 3 ... yb: 2 + 1
         33 1 ... yt: 2 - 1
         34 3 ... xr: 2 + 1
         35 1 ... xl: 2 - 1
         36 0
   bp => 37 47

問題ないようです!


さて、 右端、左端、上端、下端のそれぞれについて case文を追加することで補正をしましたが、 似た処理なのでこれは共通化しておくと良さそうです。

関数にしてみました:

--- a/33_adjust_index_2.vgt.json
+++ b/33_adjust_index_2.vgt.json
@@ -24,6 +24,27 @@
     ]
   ]
 
+, ["func", "adjust_index", ["width", "i"]
+  , [
+      ["var", "adjusted"]
+    , ["var", "max_i"]
+    , ["set", "max_i", ["+", "width", -1]]
+
+    , ["case"
+      , [["eq", "i", -1]
+        , ["_cmt", "下限を超えた場合"]
+        , ["set", "adjusted", "max_i"]]
+      , [["eq", "i", "width"]
+        , ["_cmt", "上限を超えた場合"]
+        , ["set", "adjusted", 0]]
+      , [["eq", 1, 1]
+        , ["_cmt", "補正が不要な場合"]
+        , ["set", "adjusted", "i"]]
+      ]
+    , ["return", "adjusted"]
+    ]
+  ]
+
 , ["func", "count_alive", ["w", "h", "x", "y"]
   , [
       ["var", "count"]

たとえば xl の場合は「右端を超えたか」のチェックは不要なので、 「下限の補正」「上限の補正」に分けてもいいかなという気もしましたが、 とりあえずはこれでいいんじゃないでしょうか。 気になるようであればライフゲームが動いてから好きなだけいじりましょう。

main() の先頭に適当に確認用コードを追加して、 簡単に動作確認してみます。

, ["func", "main", []
  , [
      ["var", "tmp"]

    , ["call_set", "tmp", ["adjust_index", 5, -1]]
    , ["_cmt", "★ 座標補正の確認 下端: 4 になるべき"]

    , ["call_set", "tmp", ["adjust_index", 5, 5]]
    , ["_cmt", "★ 座標補正の確認 上端: 0 になるべき"]

    , ["call_set", "tmp", ["adjust_index", 5, 1]]
    , ["_cmt", "★ 座標補正の確認 補正なし: 1 になるべき"]

    // 略

期待通り(上記のコメントで書いてある通り)に動いていることを確認できたので、 確認用コードをコメントアウトしてから、 補正処理の箇所を adjust_index() を使うように置き換えていきます。

まずは左端を超えた場合の xl の補正だけ。

--- a/33_adjust_index_2.vgt.json
+++ b/33_adjust_index_2.vgt.json
@@ -61,12 +61,7 @@
     , ["set", "yb", ["+", "y",  1]]
 
     , ["_cmt", "★ xl の補正の直前"]
-    , ["case"
-      , [["eq", "xl", -1]
-        , ["_cmt", "★ 左端を超えた場合"]
-        , ["set", "xl", ["+", "w", -1]]
-        ]
-      ]
+    , ["call_set", "xl", ["adjust_index", "w", "xl"]]
     , ["_cmt", "★ xl の補正の直後"]
 
     , ["_cmt", "★ xr の補正の直前"]
@@ -96,6 +91,8 @@
       ]
     , ["_cmt", "★ yb の補正の直後"]
 
+    , ["_cmt", "★ 座標補正の直後"]
+
     , ["var", "tmp"]
 
     , ["_cmt", "左上"]
@@ -160,7 +157,7 @@
     , ["var", "y"]
     , ["set", "y", 0]
 
-    , ["call", "count_alive", "w", "h", 2, 4]
+    , ["call", "count_alive", "w", "h", 0, 2]
     ]
   ]

ついでに、4つの補正が全部終わったところに ★ 座標補正の直後 というコメントも追加しました。

================================
72: reg_a(4) reg_b(1) reg_c(0) zf(1)
---- memory (main) ----
      238   ["set_reg_b", -1]
      240   ["add_ab"]
      241   ["cp", "reg_a", "[bp-4]"]
      244   ["set_reg_a", "[bp+5]"]
      246   ["set_reg_b", 1]
      248   ["add_ab"]
      249   ["cp", "reg_a", "[bp-5]"]
      252   ["_cmt", "★~xl~の補正の直前"]
      254   ["push", "[bp-2]"]
      256   ["push", "[bp+2]"]
      258   ["_cmt", "call_set~~adjust_index"]
      260   ["call", 77]
      262   ["add_sp", 2]
      264   ["cp", "reg_a", "[bp-2]"]
pc => 267   ["_cmt", "★~xl~の補正の直後"]
      269   ["_cmt", "★~xr~の補正の直前"]
      271   ["set_reg_a", "[bp-3]"]
      273   ["set_reg_b", "[bp+2]"]
      275   ["compare"]
      276   ["jump_eq", 284]
      278   ["set_reg_a", 0]
      280   ["jump", 288]
      282 ["label", "then_6"]
      284   ["set_reg_a", 1]
      286 ["label", "end_eq_6"]
      288   ["set_reg_b", 1]
      290   ["compare"]
      291   ["jump_eq", 297]
      293   ["jump", 306]
      295 ["label", "when_5_0"]
      297   ["_cmt", "★~右端を超えた場合"]
---- memory (stack) ----
         24 0
         25 0
         26 4
         27 4
         28 37
         29 262
         30 5
         31 -1
sp    => 32 3
         33 1
         34 1
         35 4 ... xl
         36 0
   bp => 37 47
         38 642
         39 5
         40 5

問題ないようです。 xl 以外も同じように動作確認しつつ置き換えていきましょう。

--- a/33_adjust_index_2.vgt.json
+++ b/33_adjust_index_2.vgt.json
@@ -65,12 +65,7 @@
     , ["_cmt", "★ xl の補正の直後"]
 
     , ["_cmt", "★ xr の補正の直前"]
-    , ["case"
-      , [["eq", "xr", "w"]
-        , ["_cmt", "★ 右端を超えた場合"]
-        , ["set", "xr", 0]
-        ]
-      ]
+    , ["call_set", "xr", ["adjust_index", "w", "xr"]]
     , ["_cmt", "★ xr の補正の直後"]
 
     , ["_cmt", "★ yt の補正の直前"]
@@ -157,7 +152,7 @@
     , ["var", "y"]
     , ["set", "y", 0]
 
-    , ["call", "count_alive", "w", "h", 0, 2]
+    , ["call", "count_alive", "w", "h", 4, 2]
     ]
   ]
--- a/33_adjust_index_2.vgt.json
+++ b/33_adjust_index_2.vgt.json
@@ -69,12 +69,7 @@
     , ["_cmt", "★ xr の補正の直後"]
 
     , ["_cmt", "★ yt の補正の直前"]
-    , ["case"
-      , [["eq", "yt", -1]
-        , ["_cmt", "★ 上端を超えた場合"]
-        , ["set", "yt", ["+", "h", -1]]
-        ]
-      ]
+    , ["call_set", "yt", ["adjust_index", "h", "yt"]]
     , ["_cmt", "★ yt の補正の直後"]
 
     , ["_cmt", "★ yb の補正の直前"]
@@ -152,7 +147,7 @@
     , ["var", "y"]
     , ["set", "y", 0]
 
-    , ["call", "count_alive", "w", "h", 4, 2]
+    , ["call", "count_alive", "w", "h", 2, 0]
     ]
   ]
--- a/33_adjust_index_2.vgt.json
+++ b/33_adjust_index_2.vgt.json
@@ -73,12 +73,7 @@
     , ["_cmt", "★ yt の補正の直後"]
 
     , ["_cmt", "★ yb の補正の直前"]
-    , ["case"
-      , [["eq", "yb", "h"]
-        , ["_cmt", "★ 下端を超えた場合"]
-        , ["set", "yb", 0]
-        ]
-      ]
+    , ["call_set", "yb", ["adjust_index", "h", "yb"]]
     , ["_cmt", "★ yb の補正の直後"]
 
     , ["_cmt", "★ 座標補正の直後"]
@@ -147,7 +142,7 @@
     , ["var", "y"]
     , ["set", "y", 0]
 
-    , ["call", "count_alive", "w", "h", 2, 0]
+    , ["call", "count_alive", "w", "h", 2, 4]
     ]
   ]

というわけで、座標補正処理、完成です!