digital circuit simulator (6)

とりあえず、昨晩とりあえず動いた微妙な実装を以下にサラしてみる。
とても恥ずかしい。がしかし (以下略

class Segment
  attr_accessor :t, :q
  def initialize(time, queue)
    @t = time
    @q = queue
  end
end

class Agenda
  attr_accessor :currentTime, :segments

  def initialize
    @currentTime = 0
    @segments = Array.new
  end

  def segments=(seg)
    @segments.push seg
  end

  def empty?
    @segments.size == 0
  end

  def firstAgenda
    seg = @segments.first
    @currentTime = seg.t
    seg.q.first
  end

  def removeFirst
    @segments.first.q.shift
    if @segments.first.q.size == 0
      @segments.shift
    end
  end

  def add2agenda(time, p)
    if empty? || @segments.first.t > time
      seg = Segment.new(time, Array[p])
      @segments.unshift(seg)
    else
      @segments.each_index do |i|
        if @segments[i].t == time
#          @segments[i].q.unshift(p)
          @segments[i].q.push(p)
          return
        elsif @segments[i].t > time
#         elsif @segments[i].t < time
          seg = Segment.new(time, Array[p])
#          seg = Segment.new(time, Array.new)
#          seg.q.shift(p)
#          @segments.insert(i - 1, seg)
          @segments.insert(i, seg)
          return
        end
      end
      seg = Segment.new(time, Array[p])
      @segments.push(seg)
    end
  end
end

class Wire
  def initialize
    @value = 0
    @action = Array.new
  end
  def getValue
    @value
  end
  def setValue(v)
    if @value != v
      @value = v
      @action.each do |i|
        i.call
      end
    end
  end
  def addAction(p)
    @action.unshift(p)
    p.call
  end
end

class Inverter
  attr_accessor :i, :o
  def initialize(input, output, agenda)
    @i = input; @o = output; @ag = agenda
    @d = $inverterDelay
    p = Proc.new { newVal = logicalNot(@i.getValue);
#      Sim.afterDelay(@d, lambda{@o.setValue(newVal)}) }
      afterDelay(@d, lambda{@o.setValue(newVal)}, @ag) }
#      @s.afterDelay(@d, lambda{@o.setValue(newVal)}) }
    @i.addAction p
  end

  def logicalNot(signal)
    if signal == 0
      1
    else
      0
    end
  end
end

class AndGate
#  attr_accessor :i1, :i2, :o
  def initialize(input1, input2, output, agenda)
    @i1 = input1; @i2 = input2; @o = output; @d = $andDelay; @ag = agenda
#    p = Proc.new { newVal = ((@i1.getValue == 1 && @i2.getValue == 1) ? 1 : 0);
    p = Proc.new { newVal = logicalAnd(@i1.getValue, @i2.getValue);
#      Sim.afterDelay(@d, lambda{@o.setValue(newVal)}) }
      afterDelay(@d, lambda{@o.setValue(newVal)}, @ag) }
#      @s.afterDelay(@d, lambda{@o.setValue(newVal)}) }
    @i1.addAction p
    @i2.addAction p
  end

  def logicalAnd(s1, s2)
    if s1 == 1 && s2 == 1
      1
    else
      0
    end
  end
end

class OrGate
#  attr_accessor :i1, :i2, :o
  def initialize(input1, input2, output, agenda)
    @i1 = input1; @i2 = input2; @o = output; @d = $orDelay; @ag = agenda
#    p = Proc.new { newVal = ((@i1.getValue == 0 && @i2.getValue == 0) ? 0 : 1);
    p = Proc.new { newVal = logicalOr(@i1.getValue, @i2.getValue);
#      Sim.afterDelay(@d, lambda{@o.setValue(newVal)}) }
      afterDelay(@d, lambda{@o.setValue(newVal)}, @ag) }
#      @s.afterDelay(@d, lambda{@o.setValue(newVal)}) }
    @i1.addAction p
    @i2.addAction p
  end
  def logicalOr(s1, s2)
    if s1 == 1 || s2 == 1
      1
    else
      0
    end
  end
end

class HalfAdder
#  attr_accessor :a, :b, :s, :c
  attr_accessor :d, :e
  def initialize(agenda, a, b, s, c)
    @a = a; @b = b; @s = s; @c = c; @ag = agenda
    @d = Wire.new
    @e = Wire.new
    @g1 = OrGate.new(@a, @b, @d, @ag)
    @g2 = AndGate.new(@a, @b, @c, @ag)
    @g3 = Inverter.new(@c, @e, @ag)
    @g4 = AndGate.new(@d, @e, @s, @ag)
#    @gates = Array[OrGate.new(@a, @b, @d),
#                   AndGate.new(@a, @b, @c),
#                   Inverter.new(@c, @e),
#                   AndGate.new(@d, @e, @s)]
  end
end

class FullAdder
  attr_accessor :a, :b, :cIn, :sum, :cOut
  def initialize(a, b, cIn, sum, cOut)
    @a = a; @b = b; @cIn = cIn; @sum = sum; @cOut = cOut;
    @s = Wire.new
    @c1 = Wire.new
    @c2 = Wire.new
    @gates = Array[HalfAdder.new(@b, @cIn, @s, @c1),
                   HalfAdder.new(@a, @s, @sum, @c2),
                   OrGate.new(@c1, @c2, @cOut)]
  end
end


class Sim
  attr_accessor :ag
  
  def initialize
    $inverterDelay = 2
    $andDelay = 3
    $orDelay = 5
    @ag = Agenda.new
  end

  def simulate
    @input1 = Wire.new
    @input2 = Wire.new
    @sum = Wire.new
    @carry = Wire.new
    probe("carry", @carry)
    probe("sum", @sum)
    @haobj = HalfAdder.new(@ag, @input1, @input2, @sum, @carry)
    @input1.setValue(1)
    propagate
    @input2.setValue(1)
    propagate
  end

  def probe(name, wire)
    wire.addAction(lambda {print name; print " "; print @ag.currentTime;
                     print " New-value = "; print wire.getValue; puts ""})
  end

  def propagate
    if !@ag.empty?
      @ag.firstAgenda.call
      @ag.removeFirst
      propagate
    end
  end
end

def afterDelay(delay, p, ag)
  ag.add2agenda(delay + ag.currentTime, p)
end

コメントアウトした部分もそのままサラしてやれ。不具合が出てたのは基本的に add-to-agenda! (上記実装では Agenda#add2agenda ) です。同一時刻単位に手続き追加する時に enqueue してなかったり、末端に次第書きが追加されなかったり、等とゆー微妙なバグが多くてヘコんどります。