vm2gol v2 製作メモ(35) 次世代の生死をバッファ領域に書き込み


周囲の生存セル数から次世代の生死を決定できるようになりました。 今回は、これを VRAM のバッファ領域に書き込む部分を作ります。

VRAM のメイン領域とバッファ領域は、アドレスが 25 ずれているので、 x, y 座標から算出したアドレスに 25 を足せば、 メイン領域と同じようにバッファ領域の読み書きができます。

前に作った vram_set() をコピペして、 vi に 25 を足す処理を追加して、 vram_set() のバッファ領域版を用意します。

25 というのがちょっとマジックナンバーっぽいですが、とりあえずハードコーディングです。 ここらへんは気分で。

--- a/gol.vgt.json
+++ b/gol.vgt.json
@@ -12,6 +12,20 @@
     ]
   ]
 
+, ["func", "vram_set_buf", ["w", "x", "y", "val"]
+  , [
+      ["var", "yw"]
+    , ["set", "yw", ["*", "y", "w"]]
+
+    , ["var", "vi"] // vram index
+    , ["set", "vi", ["+", "yw", "x"]]
+
+    , ["set", "vi", ["+", "vi", 25]]
+
+    , ["set", "vram[vi]", "val"]
+    ]
+  ]
+
 , ["func", "vram_get", ["w", "x", "y"]
   , [
       ["var", "yw"]

まずは vram_set_buf() 単体で確認してみましょうか。

--- a/gol.vgt.json
+++ b/gol.vgt.json
@@ -231,6 +231,15 @@
     ]
   ]
 
+, ["func", "test_vram_set_buf", []
+  , [
+      ["call", "vram_set_buf", 5, 2, 2, 1]
+    , ["_cmt", "★ (2,2) に 1 をセット"]
+    , ["call", "vram_set_buf", 5, 2, 2, 0]
+    , ["_cmt", "★ (2,2) に 0 をセット"]
+    ]
+  ]
+
 , ["func", "main", []
   , [
     //   ["var", "tmp"]
@@ -246,9 +255,11 @@
 
     //   ["call", "test_calc_next_gen"]
 
+      ["call", "test_vram_set_buf"]
+
     // ----------------
 
-      ["var", "w"] // 盤面の幅
+    , ["var", "w"] // 盤面の幅
     , ["set", "w", 5]
     , ["var", "h"] // 盤面の高さ
     , ["set", "h", 5]

(2,2) に 1 をセットした直後。 ダンプ表示の「memory (vram)」で結果が確認できます。

================================
34: reg_a(37) reg_b(25) reg_c(0) zf(0)
---- memory (main) ----
      1063   ["_cmt", "★~生-8~→~死になるはず"]
      1065   ["cp", "bp", "sp"]
      1068   ["pop", "bp"]
      1070   ["ret"]
      1071 ["label", "test_vram_set_buf"]
      1073   ["push", "bp"]
      1075   ["cp", "sp", "bp"]
      1078   ["push", 1]
      1080   ["push", 2]
      1082   ["push", 2]
      1084   ["push", 5]
      1086   ["_cmt", "call~~vram_set_buf"]
      1088   ["call", 41]
      1090   ["add_sp", 4]
pc => 1092   ["_cmt", "★~(2,2)~に~1~をセット"]
      1094   ["push", 0]
      1096   ["push", 2]
      1098   ["push", 2]
      1100   ["push", 5]
      1102   ["_cmt", "call~~vram_set_buf"]
      1104   ["call", 41]
      1106   ["add_sp", 4]
      1108   ["_cmt", "★~(2,2)~に~0~をセット"]
      1110   ["cp", "bp", "sp"]
      1113   ["pop", "bp"]
      1115   ["ret"]
      1116 ["label", "main"]
      1118   ["push", "bp"]
      1120   ["cp", "sp", "bp"]
---- memory (stack) ----
         37 37
         38 10
         39 45
         40 1090
         41 5
         42 2
         43 2
         44 1
sp bp => 45 47
         46 1127
         47 49
         48 2
         49 0
---- memory (vram) ----
..... .....
..... .....
..... ..@..
..... .....
..... .....

(2,2) に 0 をセットした直後。

================================
62: reg_a(37) reg_b(25) reg_c(0) zf(0)
---- memory (main) ----
      1078   ["push", 1]
      1080   ["push", 2]
      1082   ["push", 2]
      1084   ["push", 5]
      1086   ["_cmt", "call~~vram_set_buf"]
      1088   ["call", 41]
      1090   ["add_sp", 4]
      1092   ["_cmt", "★~(2,2)~に~1~をセット"]
      1094   ["push", 0]
      1096   ["push", 2]
      1098   ["push", 2]
      1100   ["push", 5]
      1102   ["_cmt", "call~~vram_set_buf"]
      1104   ["call", 41]
      1106   ["add_sp", 4]
pc => 1108   ["_cmt", "★~(2,2)~に~0~をセット"]
      1110   ["cp", "bp", "sp"]
      1113   ["pop", "bp"]
      1115   ["ret"]
      1116 ["label", "main"]
      1118   ["push", "bp"]
      1120   ["cp", "sp", "bp"]
      1123   ["_cmt", "call~~test_vram_set_buf"]
      1125   ["call", 1073]
      1127   ["add_sp", 0]
      1129   ["sub_sp", 1]
      1131   ["cp", 5, "[bp-1]"]
      1134   ["sub_sp", 1]
      1136   ["cp", 5, "[bp-2]"]
---- memory (stack) ----
         37 37
         38 10
         39 45
         40 1106
         41 5
         42 2
         43 2
         44 0
sp bp => 45 47
         46 1127
         47 49
         48 2
         49 0
---- memory (vram) ----
..... .....
..... .....
..... .....
..... .....
..... .....

問題ないですね!

確認が済んだので test_vram_set_buf() の呼び出しを無効化して、と。

--- a/gol.vgt.json
+++ b/gol.vgt.json
@@ -255,11 +255,11 @@
 
     //   ["call", "test_calc_next_gen"]
 
-      ["call", "test_vram_set_buf"]
+    //   ["call", "test_vram_set_buf"]
 
     // ----------------
 
-    , ["var", "w"] // 盤面の幅
+      ["var", "w"] // 盤面の幅
     , ["set", "w", 5]
     , ["var", "h"] // 盤面の高さ
     , ["set", "h", 5]

では、前回までに作った部分(次世代の生死を決定する)と くっつけて、次世代の生死がバッファ領域に書き込まれるようにしましょう。

--- a/gol.vgt.json
+++ b/gol.vgt.json
@@ -168,6 +168,7 @@
 
     , ["_cmt", "★次世代の生死決定の直後"]
 
+    , ["return", "next_val"]
     ]
   ]
 
@@ -271,12 +272,17 @@
     , ["set", "y", 0]
 
       // 初期状態の設定
-    , ["call", "vram_set", "w", 2, 2, 1]
     , ["call", "vram_set", "w", 1, 1, 1]
-    , ["call", "vram_set", "w", 1, 2, 1]
-    , ["call", "vram_set", "w", 1, 3, 1]
+    , ["call", "vram_set", "w", 2, 1, 1]
+    , ["call", "vram_set", "w", 3, 1, 1]
+
+    , ["var", "next_val"]
+
+    , ["call_set", "next_val", ["count_alive", "w", "h", 2, 2]]
+    , ["_cmt", "★ count_alive から戻った直後"]
 
-    , ["call", "count_alive", "w", "h", 2, 2]
+    , ["call", "vram_set_buf", "w", 2, 2, "next_val"]
+    , ["_cmt", "★ vram_set_buf の直後"]
     ]
   ]

実行。

================================
635: reg_a(37) reg_b(25) reg_c(0) zf(1)
---- memory (main) ----
      1194   ["push", "[bp-2]"]
      1196   ["push", "[bp-1]"]
      1198   ["_cmt", "call_set~~count_alive"]
      1200   ["call", 418]
      1202   ["add_sp", 4]
      1204   ["cp", "reg_a", "[bp-5]"]
      1207   ["_cmt", "★~count_alive~から戻った直後"]
      1209   ["push", "[bp-5]"]
      1211   ["push", 2]
      1213   ["push", 2]
      1215   ["push", "[bp-1]"]
      1217   ["_cmt", "call~~vram_set_buf"]
      1219   ["call", 41]
      1221   ["add_sp", 4]
pc => 1223   ["_cmt", "★~vram_set_buf~から戻った直後"]
      1225   ["cp", "bp", "sp"]
      1228   ["pop", "bp"]
      1230   ["ret"]
---- memory (stack) ----
         34 37
         35 10
         36 47
         37 1221
         38 5
         39 2
         40 2
         41 1
sp    => 42 1
         43 0
         44 0
         45 5
         46 5
   bp => 47 49
         48 2
         49 0
---- memory (vram) ----
..... .....
.@@@. .....
..... ..@..
..... .....
..... .....

おおぉ〜〜、まだ 1セルだけですが、ふわ〜っとライフゲーム臭が漂ってきました!! いいぞいいぞ〜!


次に進む前にリファクタリングしておきます。

VRAM のアドレス vi に変換する部分です。

  ["var", "yw"]
, ["set", "yw", ["*", "y", "w"]]

, ["var", "vi"] // vram index
, ["set", "vi", ["+", "yw", "x"]]

これが繰り返し出てくるので、関数に抽出しておきます。

to_vi() を追加して……

--- a/gol.vgt.json
+++ b/gol.vgt.json
@@ -1,5 +1,18 @@
 ["stmts"
 
+, ["func", "to_vi", ["w", "x", "y", "offset"]
+  , [
+      ["var", "yw"]
+    , ["set", "yw", ["*", "y", "w"]]
+
+    , ["var", "vi"] // vram index
+    , ["set", "vi", ["+", "yw", "x"]]
+    , ["set", "vi", ["+", "vi", "offset"]]
+
+    , ["return", "vi"]
+    ]
+  ]
+
 , ["func", "vram_set", ["w", "x", "y", "val"]
   , [
       ["var", "yw"]

to_vi() を使って vi を求めるように置き換えます。

--- a/gol.vgt.json
+++ b/gol.vgt.json
@@ -15,38 +15,24 @@
 
 , ["func", "vram_set", ["w", "x", "y", "val"]
   , [
-      ["var", "yw"]
-    , ["set", "yw", ["*", "y", "w"]]
-
-    , ["var", "vi"] // vram index
-    , ["set", "vi", ["+", "yw", "x"]]
-
+      ["var", "vi"] // vram index
+    , ["call_set", "vi", ["to_vi", "w", "x", "y", 0]]
     , ["set", "vram[vi]", "val"]
     ]
   ]
 
 , ["func", "vram_set_buf", ["w", "x", "y", "val"]
   , [
-      ["var", "yw"]
-    , ["set", "yw", ["*", "y", "w"]]
-
-    , ["var", "vi"] // vram index
-    , ["set", "vi", ["+", "yw", "x"]]
-
-    , ["set", "vi", ["+", "vi", 25]]
-
+      ["var", "vi"] // vram index
+    , ["call_set", "vi", ["to_vi", "w", "x", "y", 25]]
     , ["set", "vram[vi]", "val"]
     ]
   ]
 
 , ["func", "vram_get", ["w", "x", "y"]
   , [
-      ["var", "yw"]
-    , ["set", "yw", ["*", "y", "w"]]
-
-    , ["var", "vi"] // vram index
-    , ["set", "vi", ["+", "yw", "x"]]
-
+      ["var", "vi"] // vram index
+    , ["call_set", "vi", ["to_vi", "w", "x", "y", 0]]
     , ["return", "vram[vi]"]
     ]
   ]

もうひとつ雑に書いたところをリファクタリングしておきます。

次世代の生死決定の処理を count_alive() の中に書いてしまった (特に意図があったわけではなく、 流れでなんとなくそこに書いた、というだけです……)ため、

  • 生存セル数のカウントと生死決定という、2つのことを行っている
  • 関数名と処理内容が一致していない

という、良くない状態になってますので、これを解消しましょう。

count_alive() では名前の通り生存セル数のカウントだけを行い、 生存セル数を返すようにします。

生死決定の部分は数行だけなので、いったん main 関数に移動させておきます。

--- a/gol.vgt.json
+++ b/gol.vgt.json
@@ -153,21 +153,7 @@
 
     , ["_cmt", "★count_aliveの最後"]
 
-      // ----------------
-
-    , ["_cmt", "★次世代の生死決定の直前"]
-
-      // 注目しているセルの現世代の生死
-    , ["var", "current_val"]
-    , ["call_set", "current_val", ["vram_get", "w", "h", "x", "y"]]
-
-      // 注目しているセルの次世代の生死
-    , ["var", "next_val"]
-    , ["call_set", "next_val", ["calc_next_gen", "current_val", "count"]]
-
-    , ["_cmt", "★次世代の生死決定の直後"]
-
-    , ["return", "next_val"]
+    , ["return", "count"]
     ]
   ]
 
@@ -275,11 +261,23 @@
     , ["call", "vram_set", "w", 2, 1, 1]
     , ["call", "vram_set", "w", 3, 1, 1]
 
-    , ["var", "next_val"]
+    , ["var", "count"]
 
-    , ["call_set", "next_val", ["count_alive", "w", "h", 2, 2]]
+    , ["call_set", "count", ["count_alive", "w", "h", 2, 2]]
     , ["_cmt", "★ count_alive から戻った直後"]
 
+    , ["_cmt", "★次世代の生死決定の直前"]
+
+      // 注目しているセルの現世代の生死
+    , ["var", "current_val"]
+    , ["call_set", "current_val", ["vram_get", "w", "h", 2, 2]]
+
+      // 注目しているセルの次世代の生死
+    , ["var", "next_val"]
+    , ["call_set", "next_val", ["calc_next_gen", "current_val", "count"]]
+
+    , ["_cmt", "★次世代の生死決定の直後"]
+
     , ["call", "vram_set_buf", "w", 2, 2, "next_val"]
     , ["_cmt", "★ vram_set_buf から戻った直後"]
     ]

これで count_alive という名前の通りの処理だけを行う関数になりました。


今回はコード生成器や VM の修正はありませんでしたね。 完成まであとちょっと!!