🚥

Rubyのコードで理解するシフトレジスタのカスケード接続

2023/05/19に公開

前回と今回やること

https://zenn.dev/megeton/articles/3d2db76479c79a

前回はシリアルデータを1バイトに変換する仕組みを学んだ。今回はシフトレジスタをカスケード接続して1バイトを超えるデータを扱う仕組みをシミュレートする。

シフトレジスタ単体

class Storage
  BIT = 8

  attr_writer :input
  attr_reader :values
  attr_reader :carry

  def initialize
    @values = Array.new(BIT) { 0 }
  end

  def shift
    @values.unshift(@input)
    @carry = @values.pop
  end

  def shift_out(value)
    BIT.pred.downto(0) do |i|
      self.input = value[i]
      shift
    end
  end

  def to_i
    @values.each.with_index.sum { |e, i| e << i }
  end

  def inspect
    "%0*b (%d)" % [BIT, to_i, to_i]
  end
end

動作確認

S = Storage.new
S.shift_out(128)
S        # => 10000000 (128)
S.input = 0
S.shift
S        # => 00000000 (0)
S.carry  # => 1

左シフトすると溢れた MSB が carry に入る。

複数のシフトレジスタを繋ぐ

class BigStorage
  def initialize(count)
    @storages = Array.new(count) { Storage.new }
  end

  def shift_out(value)
    (@storages.size * Storage::BIT).pred.downto(0) do |i|
      self.input = value[i]
      shift
    end
  end

  def input=(value)
    @storages.first.input = value
  end

  def shift
    [*@storages, nil].each_cons(2) do |a, b|
      a.shift
      if b
        b.input = a.carry
      end
    end
  end

  def to_i
    @storages.flat_map(&:values).each.with_index.sum { |e, i| e << i }
  end

  def inspect
    "%0*b (%d)" % [@storages.size * Storage::BIT, to_i, to_i]
  end
end

動作確認

S = BigStorage.new(2)
S.shift_out(128)
S  # => 0000000010000000 (128)
S.input = 0
S.shift
S  # => 0000000100000000 (256)

単体のときと同様に左シフトすると MSB が2つ目のシフトレジスタに渡る。このようにシフトレジスタを増やせばいくらでも大きな数を扱えるようになる。

S = BigStorage.new(4)
S.shift_out(-1)
S  # => 11111111111111111111111111111111 (4294967295)

Discussion