vm2gol v2 製作メモ(27) ライフゲームの実装開始 / 組み込みの掛け算


なんだかライフゲーム詐欺みたいになってきましたが 今度こそほんとにライフゲームに突入できる……はず……。

というか突入しましょう!

やるぞぉ……!


はい、やります。

vgtコードです。 最終的にはグライダーを動かしたいのですが、 まずは確認が簡単そうなブリンカーから始めましょうか。

// 27_blinker.vgt.json

["stmts"
, ["func", "vram_set", ["w", "x", "y", "val"]
  , [
      ["var", "yw"]
    , ["set", "yw", ["*", "y", "w"]]

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

    , ["set", "vram[vi]", "val"]
    ]
  ]

, ["func", "main", []
  , [
      ["var", "w"] // 盤面の幅
    , ["set", "w", 5]

      // VRAM の初期化(ブリンカー)
    , ["call", "vram_set", "w", 2, 1, 1]
    , ["call", "vram_set", "w", 2, 2, 1]
    , ["call", "vram_set", "w", 2, 3, 1]
    ]
  ]
]

まずは初期化の部分ということで、 main 関数とは別に、座標を指定して状態をセットする vram_set 関数を用意し、 座標 (2, 1) (2, 2) (2, 3) の 3つのセルを生存状態にします。 VM の初期化時に VRAM の内容を 0 で初期化しているので、 これ以外のセルはすべて死亡状態です。

こういう状態に初期化したい:

.....
..@..
..@..
..@..
.....

さて、適当に書きましたが、とりあえず動かしてみましょうか。 どうなるでしょうか?

$ ./run.sh 27_blinker.vgt.json 
vgcg.rb:106:in `codegen_exp': Not yet implemented ("left") ("y") (RuntimeError)
        from vgcg.rb:154:in `codegen_set'
        from vgcg.rb:221:in `block in codegen_func_def'
        from vgcg.rb:196:in `each'
        from vgcg.rb:196:in `codegen_func_def'
        from vgcg.rb:251:in `block in codegen_stmts'
        from vgcg.rb:247:in `each'
        from vgcg.rb:247:in `codegen_stmts'
        from vgcg.rb:270:in `codegen'
        from vgcg.rb:282:in `<main>'

vgcg.rb の該当箇所を見てみると下記のようになっています。

def codegen_exp(lvar_names, exp)

  # ...

  left =
    case args[0]
    when Integer
      args[0]
    when String
      case
      when lvar_names.include?(args[0])
        lvar_pos = lvar_names.index(args[0]) + 1
        "[bp-#{lvar_pos}]"
      else
        raise not_yet_impl("left", args[0])
      end
    else
      # ...

ローカル変数には対応済ですが、関数の引数には未対応だったようです。 対応しましょう。

--- a/vgcg.rb
+++ b/vgcg.rb
@@ -65,7 +65,7 @@ def codegen_while(fn_arg_names, lvar_names, rest)
   alines << "label while_#{label_id}"
 
   # 条件の評価 ... 結果が reg_a に入る
-  alines += codegen_exp(lvar_names, cond_exp)
+  alines += codegen_exp(fn_arg_names, lvar_names, cond_exp)
   # 比較対象の値(真)をセット
   alines << "  set_reg_b 1"
   alines << "  compare"
@@ -89,7 +89,7 @@ def codegen_while(fn_arg_names, lvar_names, rest)
   alines
 end
 
-def codegen_exp(lvar_names, exp)
+def codegen_exp(fn_arg_names, lvar_names, exp)
   alines = []
   operator, *args = exp
 
@@ -102,6 +102,9 @@ def codegen_exp(lvar_names, exp)
       when lvar_names.include?(args[0])
         lvar_pos = lvar_names.index(args[0]) + 1
         "[bp-#{lvar_pos}]"
+      when fn_arg_names.include?(args[0])
+        fn_arg_pos = fn_arg_names.index(args[0]) + 2
+        "[bp+#{fn_arg_pos}]"
       else
         raise not_yet_impl("left", args[0])
       end
@@ -151,7 +154,7 @@ def codegen_set(fn_arg_names, lvar_names, rest)
       rest[1]
     when rest[1].is_a?(Array)
       exp = rest[1]
-      alines += codegen_exp(lvar_names, exp)
+      alines += codegen_exp(fn_arg_names, lvar_names, exp)
       "reg_a"
     when fn_arg_names.include?(rest[1])
       fn_arg_pos = fn_arg_names.index(rest[1]) + 2
@@ -220,7 +223,7 @@ def codegen_func_def(rest)
     when "set"
       alines += codegen_set(fn_arg_names, lvar_names, stmt_rest)
     when "eq"
-      alines += codegen_exp(lvar_names, stmt)
+      alines += codegen_exp(fn_arg_names, lvar_names, stmt)
     when "return"
       val = stmt_rest[0]
       alines << "  set_reg_a #{val}"

再度実行。

$ ./run.sh 27_blinker.vgt.json 
vgcg.rb:141:in `codegen_exp': Not yet implemented ("operator") ("*") (RuntimeError)
    from vgcg.rb:157:in `codegen_set'
    from vgcg.rb:224:in `block in codegen_func_def'
    from vgcg.rb:199:in `each'
    from vgcg.rb:199:in `codegen_func_def'
    from vgcg.rb:254:in `block in codegen_stmts'
    from vgcg.rb:250:in `each'
    from vgcg.rb:250:in `codegen_stmts'
    from vgcg.rb:273:in `codegen'
    from vgcg.rb:285:in `<main>'

おっと、掛け算がまだなかったですね。 足し算と同じ感じで追加しましょう。

まず VM に命令を追加してから、それに合わせてコード生成器を修正する、 という順番が正攻法な気がしますが、 そんなに難しいことやってないのでコード生成器から先にやってしまっても大丈夫でしょう。

エラーメッセージにしたがってコード生成器を掛け算に対応させます。

--- a/vgcg.rb
+++ b/vgcg.rb
@@ -119,6 +119,10 @@ def codegen_exp(fn_arg_names, lvar_names, exp)
     alines << "  set_reg_a #{left}"
     alines << "  set_reg_b #{right}"
     alines << "  add_ab"
+  when "*"
+    alines << "  set_reg_a #{left}"
+    alines << "  set_reg_b #{right}"
+    alines << "  mult_ab"
   when "eq"
     $label_id += 1
     label_id = $label_id

足し算とほぼ同じですね。 reg_areg_b に値をセットし、 mult_ab 命令を実行すると結果が reg_a に入るようにします。 "mult" は "multiply" の略です。

実行するとこんどは VMmult_ab 命令などない! と怒られるので、 命令を追加します。

--- a/vgvm.rb
+++ b/vgvm.rb
@@ -173,6 +173,9 @@ class Vm
       when "add_ac"
         add_ac()
         @pc += pc_delta
+      when "mult_ab"
+        mult_ab()
+        @pc += pc_delta
       when "add_sp"
         set_sp(@sp + @mem.main[@pc + 1])
         @pc += pc_delta
@@ -294,7 +297,7 @@ class Vm
       2
     when "set_reg_a", "set_reg_b", "label", "call", "push", "pop", "add_sp", "sub_sp", "jump_eq", "jump"
       1
-    when "ret", "exit", "add_ab", "compare"
+    when "ret", "exit", "add_ab", "compare", "mult_ab"
       0
     else
       raise "Invalid operator (#{operator})"
@@ -346,6 +349,10 @@ class Vm
     @reg_a = @reg_a + @reg_c
   end
 
+  def mult_ab
+    @reg_a = @reg_a * @reg_b
+  end
+
   def set_reg_a(val)
     @reg_a =
       case val

こちらも足し算のコードをコピペしてちょこっと変えるだけですね。 動かしてみます。

$ ./run.sh 27_blinker.vgt.json 
(略)
================================
reg_a(0) reg_b(0) reg_c(0) zf(0)
---- memory (main) ----
      00   ["call", 41]
      02   ["exit"]
      03 ["label", "vram_set"]
      05   ["push", "bp"]
      07   ["cp", "sp", "bp"]
      10   ["sub_sp", 1]
      12   ["set_reg_a", "[bp+4]"]
      14   ["set_reg_b", "w"]
      16   ["mult_ab"]
      17   ["cp", "reg_a", "[bp-1]"]
      20   ["sub_sp", 1]
      22   ["set_reg_a", "[bp-1]"]
      24   ["set_reg_b", "x"]
      26   ["add_ab"]
      27   ["cp", "reg_a", "[bp-2]"]
      30   ["set_vram", "vi", "[bp+5]"]
      33   ["cp", "bp", "sp"]
      36   ["pop", "bp"]
      38   ["ret"]
      39 ["label", "main"]
      41   ["push", "bp"]
      43   ["cp", "sp", "bp"]
      46   ["sub_sp", 1]
      48   ["cp", 5, "[bp-1]"]
      51   ["push", 1]
      53   ["push", 1]
      55   ["push", 2]
pc => 57   ["push", "w"]
      59   ["call", 5]
      61   ["add_sp", 4]
      63   ["push", 1]
      65   ["push", 2]
      67   ["push", 2]
      69   ["push", "w"]
      71   ["call", 5]
      73   ["add_sp", 4]
      75   ["push", 1]
      77   ["push", 3]
      79   ["push", 2]
      81   ["push", "w"]
      83   ["call", 5]
      85   ["add_sp", 4]
      87   ["cp", "bp", "sp"]
      90   ["pop", "bp"]
      92   ["ret"]
---- memory (stack) ----
         35 0
         36 0
         37 0
         38 0
         39 0
         40 0
         41 0
         42 0
sp    => 43 2
         44 1
         45 1
         46 5
   bp => 47 49
         48 2
         49 0
---- memory (vram) ----
..... .....
..... .....
..... .....
..... .....
..... .....

vgvm.rb:214:in `block in start': Not yet implemented ("push") ("w") (RuntimeError)
    from vgvm.rb:142:in `loop'
    from vgvm.rb:142:in `start'
    from vgvm.rb:389:in `<main>'

機械語コードに w が出てくるのはおかしいので、 コード生成器がおかしいはず。 w の参照が解決できてないのでは。

      39 ["label", "main"]
      41   ["push", "bp"]
      43   ["cp", "sp", "bp"]
      46   ["sub_sp", 1]
      48   ["cp", 5, "[bp-1]"]
      51   ["push", 1]
      53   ["push", 1]
      55   ["push", 2]
pc => 57   ["push", "w"]
      59   ["call", 5]

main() が始まって、 vram_set() の呼び出しの前に引数を スタックに push するとこがおかしいようだ、と当たりを付けて調べてみると……。

ここですね。 やはり、参照の解決をしていないようです。

def codegen_func_def(rest)

  # ...

    case stmt_head
    when "call"
      fn_name, *fn_args = stmt_rest
      fn_args.reverse.each {|fn_arg|
        alines << "  push #{fn_arg}" # ←ここ
      }
      alines << "  call #{fn_name}"
      alines << "  add_sp #{fn_args.size}"
    when "call_set"

修正します。

--- a/vgcg.rb
+++ b/vgcg.rb
@@ -206,7 +206,20 @@ def codegen_func_def(rest)
     when "call"
       fn_name, *fn_args = stmt_rest
       fn_args.reverse.each {|fn_arg|
-        alines << "  push #{fn_arg}"
+        case fn_arg
+        when Integer
+          alines << "  push #{fn_arg}"
+        when String
+          case
+          when lvar_names.include?(fn_arg)
+            lvar_pos = lvar_names.index(fn_arg) + 1
+            alines << "  push [bp-#{lvar_pos}]"
+          else
+            raise not_yet_impl(fn_arg)
+          end
+        else
+          raise not_yet_impl(fn_arg)
+        end
       }
       alines << "  call #{fn_name}"
       alines << "  add_sp #{fn_args.size}"

動かします。

$ ./run.sh 27_blinker.vgt.json
(略)
================================
reg_a(0) reg_b(0) reg_c(0) zf(0)
---- memory (main) ----
      00   ["call", 41]
      02   ["exit"]
      03 ["label", "vram_set"]
      05   ["push", "bp"]
      07   ["cp", "sp", "bp"]
      10   ["sub_sp", 1]
      12   ["set_reg_a", "[bp+4]"]
      14   ["set_reg_b", "w"]
      16   ["mult_ab"]
      17   ["cp", "reg_a", "[bp-1]"]
      20   ["sub_sp", 1]
      22   ["set_reg_a", "[bp-1]"]
      24   ["set_reg_b", "x"]
      26   ["add_ab"]
      27   ["cp", "reg_a", "[bp-2]"]
      30   ["set_vram", "vi", "[bp+5]"]
      33   ["cp", "bp", "sp"]
      36   ["pop", "bp"]
      38   ["ret"]
      39 ["label", "main"]
      41   ["push", "bp"]
      43   ["cp", "sp", "bp"]
      46   ["sub_sp", 1]
      48   ["cp", 5, "[bp-1]"]
      51   ["push", 1]
      53   ["push", 1]
      55   ["push", 2]
pc => 57   ["push", "[bp-1]"]
      59   ["call", 5]
      61   ["add_sp", 4]
      63   ["push", 1]
      65   ["push", 2]
      67   ["push", 2]
      69   ["push", "[bp-1]"]
      71   ["call", 5]
      73   ["add_sp", 4]
      75   ["push", 1]
      77   ["push", 3]
      79   ["push", 2]
      81   ["push", "[bp-1]"]
      83   ["call", 5]
      85   ["add_sp", 4]
      87   ["cp", "bp", "sp"]
      90   ["pop", "bp"]
      92   ["ret"]
---- memory (stack) ----
         35 0
         36 0
         37 0
         38 0
         39 0
         40 0
         41 0
         42 0
sp    => 43 2
         44 1
         45 1
         46 5
   bp => 47 49
         48 2
         49 0
---- memory (vram) ----
..... .....
..... .....
..... .....
..... .....
..... .....

vgvm.rb:214:in `block in start': Not yet implemented ("push") ("[bp-1]") (RuntimeError)
    from vgvm.rb:142:in `loop'
    from vgvm.rb:142:in `start'
    from vgvm.rb:389:in `<main>'

たぶんこれも push 命令で参照の解決が未実装ですねー。

今までサボッていたツケが回ってきた、と見ることもできますが、 YAGNI でやってるので今が実装のタイミングなのだ、と見ることもできるので、 大丈夫だと思います。 たぶん。

--- a/vgvm.rb
+++ b/vgvm.rb
@@ -208,8 +208,16 @@ class Vm
           case arg
           when Integer
             arg
-          when "bp"
-            @bp
+          when String
+            case arg
+            when "bp"
+              @bp
+            when /^\[bp\-(\d+)\]$/
+              stack_addr = @bp - $1.to_i
+              @mem.stack[stack_addr]
+            else
+              raise not_yet_impl("push", arg)
+            end
           else
             raise not_yet_impl("push", arg)
           end

お次はこう:

vgvm.rb:373:in `set_reg_a': Not yet implemented ("val") ("[bp+4]") (RuntimeError)
    from vgvm.rb:154:in `block in start'
    from vgvm.rb:142:in `loop'
    from vgvm.rb:142:in `start'
    from vgvm.rb:397:in `<main>'

修正しまーす。

# Vm#set_reg_a()

--- a/vgvm.rb
+++ b/vgvm.rb
@@ -369,6 +369,9 @@ class Vm
       when /^\[bp-(\d+)\]$/
         stack_addr = @bp - $1.to_i
         @mem.stack[stack_addr]
+      when /^\[bp\+(\d+)\]$/
+        stack_addr = @bp + $1.to_i
+        @mem.stack[stack_addr]
       else
         raise not_yet_impl("val", val)
       end

またこれ:

      03 ["label", "vram_set"]
      05   ["push", "bp"]
      07   ["cp", "sp", "bp"]
      10   ["sub_sp", 1]
      12   ["set_reg_a", "[bp+4]"]
      14   ["set_reg_b", "w"] ← まだ "w" が残ってる
pc => 16   ["mult_ab"]

vgvm.rb:361:in `*': String can't be coerced into Fixnum (TypeError)
    from vgvm.rb:361:in `mult_ab'
    from vgvm.rb:177:in `block in start'
    from vgvm.rb:142:in `loop'
    from vgvm.rb:142:in `start'
    from vgvm.rb:400:in `<main>'

次から次に出てきますね……ガンガンやっつけていきましょう。 codegen_exp() の右項で参照の解決が未実装でした。

--- a/vgcg.rb
+++ b/vgcg.rb
@@ -112,7 +112,21 @@ def codegen_exp(fn_arg_names, lvar_names, exp)
       raise not_yet_impl("left", args[0])
     end
 
-  right = args[1]
+  right =
+    case args[1]
+    when Integer
+      args[1]
+    when String
+      case
+      when fn_arg_names.include?(args[1])
+        fn_arg_pos = fn_arg_names.index(args[1]) + 2
+        "[bp+#{fn_arg_pos}]"
+      else
+        raise not_yet_impl("right", args[1])
+      end
+    else
+      raise not_yet_impl("right", args[1])
+    end
 
   case operator
   when "+"

またなんか似た感じの:

================================
reg_a(1) reg_b("[bp+2]") reg_c(0) zf(0)
---- memory (main) ----
      00   ["call", 41]
      02   ["exit"]
      03 ["label", "vram_set"]
      05   ["push", "bp"]
      07   ["cp", "sp", "bp"]
      10   ["sub_sp", 1]
      12   ["set_reg_a", "[bp+4]"]
      14   ["set_reg_b", "[bp+2]"]
pc => 16   ["mult_ab"]

vgvm.rb:361:in `*': String can't be coerced into Fixnum (TypeError)
    from vgvm.rb:361:in `mult_ab'
    from vgvm.rb:177:in `block in start'
    from vgvm.rb:142:in `loop'
    from vgvm.rb:142:in `start'
    from vgvm.rb:400:in `<main>'

w[bp+2] になりましたが、 こんどは reg_b[bp+2] という文字列がそのまま入っていてダメです。

見てみるとこうなっていて、

  def start
    # ...
      case op
      # ...
      when "set_reg_a"
        val = @mem.main[@pc + 1]
        set_reg_a(val)
        @pc += pc_delta
      when "set_reg_b"
        n = @mem.main[@pc + 1]
        @reg_b = n
        @pc += pc_delta

# ...

  def set_reg_a(val)
    @reg_a =
      case val
      when Integer
        val
      when /^\[bp-(\d+)\]$/
        stack_addr = @bp - $1.to_i
        @mem.stack[stack_addr]
      when /^\[bp\+(\d+)\]$/
        stack_addr = @bp + $1.to_i
        @mem.stack[stack_addr]
      else
        raise not_yet_impl("val", val)
      end
  end

set_reg_a 命令では対応済なので、 set_reg_b でも同じようにすればいいでしょう。

--- a/vgvm.rb
+++ b/vgvm.rb
@@ -154,8 +154,8 @@ class Vm
         set_reg_a(val)
         @pc += pc_delta
       when "set_reg_b"
-        n = @mem.main[@pc + 1]
-        @reg_b = n
+        val = @mem.main[@pc + 1]
+        set_reg_b(val)
         @pc += pc_delta
       when "set_reg_c"
         n = @mem.main[@pc + 1]
@@ -377,6 +377,22 @@ class Vm
       end
   end
 
+  def set_reg_b(val)
+    @reg_b =
+      case val
+      when Integer
+        val
+      when /^\[bp-(\d+)\]$/
+        stack_addr = @bp - $1.to_i
+        @mem.stack[stack_addr]
+      when /^\[bp\+(\d+)\]$/
+        stack_addr = @bp + $1.to_i
+        @mem.stack[stack_addr]
+      else
+        raise not_yet_impl("val", val)
+      end
+  end
+
   def compare
     @zf = (@reg_a == @reg_b) ? 1 : 0
   end

お次は?

---- memory (main) ----
      00   ["call", 41]
      02   ["exit"]
      03 ["label", "vram_set"]
      05   ["push", "bp"]
      07   ["cp", "sp", "bp"]
      10   ["sub_sp", 1]
      12   ["set_reg_a", "[bp+4]"]
      14   ["set_reg_b", "[bp+2]"]
      16   ["mult_ab"]
      17   ["cp", "reg_a", "[bp-1]"]
      20   ["sub_sp", 1]
      22   ["set_reg_a", "[bp-1]"]
      24   ["set_reg_b", "[bp+3]"]
      26   ["add_ab"]
      27   ["cp", "reg_a", "[bp-2]"]
pc => 30   ["set_vram", "vi", "[bp+5]"]
      33   ["cp", "bp", "sp"]
      36   ["pop", "bp"]
      38   ["ret"]

vgvm.rb:241:in `[]=': no implicit conversion of String into Integer (TypeError)
    from vgvm.rb:241:in `block in start'
    from vgvm.rb:142:in `loop'
    from vgvm.rb:142:in `start'
    from vgvm.rb:416:in `<main>'

vi は変数名なので機械語コードに出現しているのがおかしい、 というパターンですね。 つまり参照が未解決……。

codegen_set() を修正します。

--- a/vgcg.rb
+++ b/vgcg.rb
@@ -188,7 +188,15 @@ def codegen_set(fn_arg_names, lvar_names, rest)
   case dest
   when /^vram\[(.+)\]$/
     vram_addr = $1
-    alines << "  set_vram #{vram_addr} #{src_val}"
+    case
+    when /^\d+$/ =~ vram_addr
+      alines << "  set_vram #{vram_addr} #{src_val}"
+    when lvar_names.include?(vram_addr)
+      lvar_pos = lvar_names.index(vram_addr) + 1
+      alines << "  set_vram [bp-#{lvar_pos}] #{src_val}"
+    else
+      raise not_yet_impl("vram_addr", vram_addr)
+    end
   else
     lvar_pos = lvar_names.index(dest) + 1
     alines << "  cp #{src_val} [bp-#{lvar_pos}]"

どうでしょうか?

---- memory (main) ----
      00   ["call", 41]
      02   ["exit"]
      03 ["label", "vram_set"]
      05   ["push", "bp"]
      07   ["cp", "sp", "bp"]
      10   ["sub_sp", 1]
      12   ["set_reg_a", "[bp+4]"]
      14   ["set_reg_b", "[bp+2]"]
      16   ["mult_ab"]
      17   ["cp", "reg_a", "[bp-1]"]
      20   ["sub_sp", 1]
      22   ["set_reg_a", "[bp-1]"]
      24   ["set_reg_b", "[bp+3]"]
      26   ["add_ab"]
      27   ["cp", "reg_a", "[bp-2]"]
pc => 30   ["set_vram", "[bp-2]", "[bp+5]"]

vgvm.rb:241:in `[]=': no implicit conversion of String into Integer (TypeError)
    from vgvm.rb:241:in `block in start'
    from vgvm.rb:142:in `loop'
    from vgvm.rb:142:in `start'
    from vgvm.rb:416:in `<main>'

vi[bp-2] に置き換わりましたが、まだダメか〜。

修正するぞ〜。

# set_vram

--- a/vgvm.rb
+++ b/vgvm.rb
@@ -238,7 +238,27 @@ class Vm
         arg1 = @mem.main[@pc + 1]
         arg2 = @mem.main[@pc + 2]
 
-        @mem.vram[arg1] = arg2
+        src_val =
+          case arg2
+          when Integer
+            arg2
+          when /^\[bp\+(\d+)\]$/
+            stack_addr = @bp + $1.to_i
+            @mem.stack[stack_addr]
+          else
+            raise not_yet_impl("set_vram", arg2)
+          end
+
+        case arg1
+        when Integer
+          @mem.vram[arg1] = src_val
+        when /^\[bp-(\d+)\]$/
+          stack_addr = @bp - $1.to_i
+          vram_addr = @mem.stack[stack_addr]
+          @mem.vram[vram_addr] = src_val
+        else
+          raise not_yet_impl("set_vram", arg1)
+        end
 
         @pc += pc_delta
       when "get_vram"

どうだ!?

$ ./run.sh 27_blinker.vgt.json 
(略)

================================
reg_a(17) reg_b(2) reg_c(0) zf(0)
---- memory (main) ----
      00   ["call", 41]
pc => 02   ["exit"]
      03 ["label", "vram_set"]
      05   ["push", "bp"]
      07   ["cp", "sp", "bp"]
      10   ["sub_sp", 1]
      12   ["set_reg_a", "[bp+4]"]
      14   ["set_reg_b", "[bp+2]"]
      16   ["mult_ab"]
      17   ["cp", "reg_a", "[bp-1]"]
      20   ["sub_sp", 1]
      22   ["set_reg_a", "[bp-1]"]
      24   ["set_reg_b", "[bp+3]"]
      26   ["add_ab"]
      27   ["cp", "reg_a", "[bp-2]"]
      30   ["set_vram", "[bp-2]", "[bp+5]"]
      33   ["cp", "bp", "sp"]
      36   ["pop", "bp"]
      38   ["ret"]
      39 ["label", "main"]
      41   ["push", "bp"]
      43   ["cp", "sp", "bp"]
      46   ["sub_sp", 1]
      48   ["cp", 5, "[bp-1]"]
      51   ["push", 1]
      53   ["push", 1]
      55   ["push", 2]
      57   ["push", "[bp-1]"]
      59   ["call", 5]
      61   ["add_sp", 4]
      63   ["push", 1]
      65   ["push", 2]
      67   ["push", 2]
      69   ["push", "[bp-1]"]
      71   ["call", 5]
      73   ["add_sp", 4]
      75   ["push", 1]
      77   ["push", 3]
      79   ["push", 2]
      81   ["push", "[bp-1]"]
      83   ["call", 5]
      85   ["add_sp", 4]
      87   ["cp", "bp", "sp"]
      90   ["pop", "bp"]
      92   ["ret"]
---- memory (stack) ----
         41 85
         42 5
         43 2
         44 3
         45 1
         46 5
         47 49
         48 2
sp bp => 49 0
---- memory (vram) ----
..... .....
..@.. .....
..@.. .....
..@.. .....
..... .....

exit

やった〜動いた〜!!

(上に貼ったのは終了時の状態です)

Enter キーを連打してプログラムの実行を進めると、 3つのセルが順番に生存状態になっていくのが VRAM のダンプ表示で確認できました!!!!

グラフィックが動くと、 これまでとは違う種類の手応えがあっていいですね……!!

楽しくなってきた!!


とりあえず動くところまで持って行きたかったので ほぼコピペみたいな感じで参照を解決する部分のコードを書き散らかしてしまいました。

似たような処理がこれだけ多く出現すると さすがにリファクタリングしたくなってきますので、 次回はリファクタリングにしましょう。

あと、ダンプ表示が長くなってきて1画面に収まらなくなってきたので、 そこもどうにかしましょう。