- 目次ページに戻る / 前 / 次
- 前回からの差分:
https://github.com/sonota88/vm2gol-v2/compare/01...02
第一回はさすがにあっさりすぎたのでどんどんやっていきます。
今の状態では vm = Vm.new
した後に vm
に対して外部から指示してあれこれやらせていて、
あんまり VM というかコンピュータっぽくない(最初に起動した後は勝手に動いてほしい)ので、それっぽくしていきます。
まずは命令をメモリに置いて、それを順次処理していくようにします。 そのためにプログラムカウンタも導入します。
まずはプログラムをメモリに直に書くようにして
--- a/vgvm.rb +++ b/vgvm.rb @@ -8,7 +8,11 @@ class Vm @reg_b = 0 @reg_c = 0 - @mem = Array.new(8, 0) # サイズ8。0で初期化。 + @mem = [ + "set_reg_a", 1, + "set_reg_a", 0 + ] + end end def set_mem(addr, n) @@ -35,14 +39,3 @@ end vm = Vm.new pp vm # 初期状態 -vm.set_mem(0, 1) -vm.set_mem(1, 2) -pp vm - -vm.copy_mem_to_reg_a(0) -vm.copy_mem_to_reg_b(1) -pp vm - -vm.add_ab -vm.copy_reg_c_to_mem(2) -pp vm
@pc
と Vm#start
を追加:
--- a/vgvm.rb +++ b/vgvm.rb @@ -3,6 +3,9 @@ require 'pp' class Vm def initialize + # program counter + @pc = 0 + # register @reg_a = 0 @reg_b = 0 @@ -13,6 +16,24 @@ class Vm "set_reg_a", 0 ] end + + def start + loop do + # operator + op = @mem[@pc] + case op + when "set_reg_a" + n = @mem[@pc + 1] + @reg_a = n + @pc += 2 + else + raise "Unknown operator (#{op})" + end + + # 1命令実行するごとにダンプしてちょっと待つ + pp self + sleep 1 + end end def set_mem(addr, n) @@ -39,3 +60,4 @@ end vm = Vm.new pp vm # 初期状態 +vm.start
メモリ上では「"set_reg_a"
」と「1」の2つなので、
set_reg_a
した後はプログラムカウンタを2つ進めます。
これを動かすと……
$ ruby vgvm.rb #<Vm:0x0056111dbfd248 @mem=["set_reg_a", 1, "set_reg_a", 0], @pc=0, @reg_a=0, @reg_b=0, @reg_c=0> #<Vm:0x0056111dbfd248 @mem=["set_reg_a", 1, "set_reg_a", 0], @pc=2, @reg_a=1, … ★1 @reg_b=0, @reg_c=0> #<Vm:0x0056111dbfd248 @mem=["set_reg_a", 1, "set_reg_a", 0], @pc=4, @reg_a=0, … ★2 @reg_b=0, @reg_c=0> vgvm.rb:30:in `block in start': Unknown operator () (RuntimeError) from vgvm.rb:21:in `loop' from vgvm.rb:21:in `start' from vgvm.rb:67:in `<main>'
reg_a
が ★1 で 1 に, ★2 で 0 になっているのが確認できますね!
プログラムカウンタも2つずつ進んでいます。
これで、
- プログラムカウンタが指している場所から命令と引数を取ってくる
- 命令を実行する
- プログラムカウンタを進める
- (くりかえし)
という VM の骨組みができました。 実装としては case による条件分岐をループで回す、という形になっていて、後はこれに肉付けしていくことになります。
最後に例外が発生して終了するのはプログラム通りの動きではありますが、
exit
という命令を追加して、例外を発生させずに静かに終了するようにしておきます。
--- a/vgvm.rb +++ b/vgvm.rb @@ -13,7 +13,8 @@ class Vm @mem = [ "set_reg_a", 1, - "set_reg_a", 0 + "set_reg_a", 0, + "exit" ] end @@ -22,6 +23,9 @@ class Vm # operator op = @mem[@pc] case op + when "exit" + $stderr.puts "exit" + exit when "set_reg_a" n = @mem[@pc + 1] set_reg_a(n)