logicuit 0.1.5 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Logicuit
4
+ module Circuits
5
+ module Sequential
6
+ # Program Counter
7
+ class ProgramCounter < DSL
8
+ diagram <<~DIAGRAM
9
+ (A)---| |---(QA)
10
+ (B)---| |---(QB)
11
+ (C)---|PC|---(QC)
12
+ (D)---| |---(QD)
13
+ (LD)--| |
14
+ DIAGRAM
15
+
16
+ inputs :a, :b, :c, :d, :ld, clock: :ck
17
+
18
+ outputs :qa, :qb, :qc, :qd
19
+
20
+ assembling do
21
+ # inputs :cin, :a0, :b0, :a1, :b1, :a2, :b2, :a3, :b3
22
+ fadd = Combinational::FullAdder4bit.new(0, 0, 1, 0, 0, 0, 0, 0, 0)
23
+
24
+ [[a, qa, :a0, :s0], [b, qb, :a1, :s1], [c, qc, :a2, :s2],
25
+ [d, qd, :a3, :s3]].each do |input, output, fadd_in, fadd_out|
26
+ dff = Sequential::DFlipFlop.new
27
+ mux = Combinational::Multiplexer2to1.new
28
+ input >> mux.c0
29
+ dff.q >> fadd.send(fadd_in)
30
+ fadd.send(fadd_out) >> mux.c1
31
+ ld >> mux.a
32
+ mux.y >> dff.d
33
+ dff.q >> output
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Logicuit
4
+ module Circuits
5
+ module Sequential
6
+ # 4 bit register
7
+ class Register4bit < DSL
8
+ diagram <<~DIAGRAM
9
+ +---------------------+
10
+ +-| | |
11
+ (A)-------|MUX|-------|DFF|---+---(QA)
12
+ +---| | +---| |
13
+ | |
14
+ | +---------------------+
15
+ | +-| | | |
16
+ (B)-------|MUX|-------|DFF|---+---(QB)
17
+ +---| | +---| |
18
+ | |
19
+ | +---------------------+
20
+ | +-| | | |
21
+ (C)-------|MUX|-------|DFF|---+---(QC)
22
+ +---| | +---| |
23
+ | |
24
+ | +---------------------+
25
+ | +-| | | |
26
+ (D)-------|MUX|-------|DFF|---+---(QD)
27
+ +---| | +---| |
28
+ (LD)--+ (CK)--+
29
+ DIAGRAM
30
+
31
+ inputs :a, :b, :c, :d, :ld, clock: :ck
32
+
33
+ outputs :qa, :qb, :qc, :qd
34
+
35
+ assembling do
36
+ [[a, qa], [b, qb], [c, qc], [d, qd]].each do |input, output|
37
+ dff = Sequential::DFlipFlop.new
38
+ mux = Combinational::Multiplexer2to1.new
39
+ input >> mux.c0
40
+ dff.q >> mux.c1
41
+ ld >> mux.a
42
+ mux.y >> dff.d
43
+ dff.q >> output
44
+ end
45
+ end
46
+
47
+ truth_table <<~TRUTH_TABLE
48
+ | CK | A | B | C | D | LD | QA | QB | QC | QD |
49
+ | -- | - | - | - | - | -- | -- | -- | -- | -- |
50
+ | ^ | x | x | x | x | 0 | A | B | C | D |
51
+ | ^ | x | x | x | x | 1 | QA | QB | QC | QD |
52
+ TRUTH_TABLE
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Logicuit
4
+ module Circuits
5
+ module Td4
6
+ # TD4 CPU
7
+ class Cpu < DSL
8
+ using Logicuit::ArrayAsSignalGroup
9
+
10
+ inputs :in0, :in1, :in2, :in3, clock: :ck
11
+
12
+ outputs :led1, :led2, :led3, :led4
13
+
14
+ assembling do
15
+ register_a, register_b, register_c = (:a..:c).map { Sequential::Register4bit.new }
16
+ pc = Sequential::ProgramCounter.new
17
+ rom = Rom.new
18
+ dec = Decoder.new
19
+ mux0, mux1, mux2, mux3 = (0..3).map { Combinational::Multiplexer4to1.new }
20
+ alu = Combinational::FullAdder4bit.new
21
+ dff = Sequential::DFlipFlop.new
22
+
23
+ alu.s0 >> [register_a.a, register_b.a, register_c.a, pc.a]
24
+ alu.s1 >> [register_a.b, register_b.b, register_c.b, pc.b]
25
+ alu.s2 >> [register_a.c, register_b.c, register_c.c, pc.c]
26
+ alu.s3 >> [register_a.d, register_b.d, register_c.d, pc.d]
27
+ alu.c >> dff.d
28
+
29
+ [register_a.qa, register_b.qa, in0] >> mux0[:c0, :c1, :c2]
30
+ [register_a.qb, register_b.qb, in1] >> mux1[:c0, :c1, :c2]
31
+ [register_a.qc, register_b.qc, in2] >> mux2[:c0, :c1, :c2]
32
+ [register_a.qd, register_b.qd, in3] >> mux3[:c0, :c1, :c2]
33
+ dec.sel_a >> [mux0.a, mux1.a, mux2.a, mux3.a]
34
+ dec.sel_b >> [mux0.b, mux1.b, mux2.b, mux3.b]
35
+ [mux0.y, mux1.y, mux2.y, mux3.y] >> [alu.a0, alu.a1, alu.a2, alu.a3]
36
+
37
+ register_c[:qa, :qb, :qc, :qd] >> [led4, led3, led2, led1]
38
+ pc[:qa, :qb, :qc, :qd] >> rom[:a0, :a1, :a2, :a3]
39
+ rom[:d0, :d1, :d2, :d3] >> alu[:b0, :b1, :b2, :b3]
40
+ rom[:d4, :d5, :d6, :d7] >> dec[:op0, :op1, :op2, :op3]
41
+ dec[:ld0, :ld1, :ld2, :ld3] >> [register_a.ld, register_b.ld, register_c.ld, pc.ld]
42
+ dff.q >> dec.c_flag
43
+
44
+ [register_a, register_b, pc, rom, dec]
45
+ end
46
+
47
+ def to_s
48
+ p_a = "(#{@a || "0000"})"
49
+ p_b = "(#{@b || "0000"})"
50
+
51
+ register_a, register_b, pc, rom, dec = components
52
+ @a = a = register_a[:qd, :qc, :qb, :qa].to_s
53
+ @b = b = register_b[:qd, :qc, :qb, :qa].to_s
54
+ p = pc[:qd, :qc, :qb, :qa]
55
+ o = self[:led1, :led2, :led3, :led4]
56
+ i = self[:in3, :in2, :in1, :in0]
57
+ m = rom[:d3, :d2, :d1, :d0]
58
+ c = "-(#{dec.c_flag})"
59
+ loc = p.to_s.to_i(2)
60
+
61
+ l1 = led1.current ? "*" : " "
62
+ l2 = led2.current ? "*" : " "
63
+ l3 = led3.current ? "*" : " "
64
+ l4 = led4.current ? "*" : " "
65
+
66
+ <<~OUTPUT
67
+
68
+ #{l1 * 7}#{" " * 7}#{l2 * 7}#{" " * 7}#{l3 * 7}#{" " * 7}#{l4 * 7}
69
+ #{l1 * 9}#{" " * 5}#{l2 * 9}#{" " * 5}#{l3 * 9}#{" " * 5}#{l4 * 9}
70
+ #{l1 * 11}#{" " * 3}#{l2 * 11}#{" " * 3}#{l3 * 11}#{" " * 3}#{l4 * 11}
71
+ #{l1 * 9}#{" " * 5}#{l2 * 9}#{" " * 5}#{l3 * 9}#{" " * 5}#{l4 * 9}
72
+ #{l1 * 7}#{" " * 7}#{l2 * 7}#{" " * 7}#{l3 * 7}#{" " * 7}#{l4 * 7}
73
+
74
+ +-----------------------------------------------+ #{loc == 0 ? ">" : " "} OUT 0111
75
+ | | #{loc == 1 ? ">" : " "} ADD A,0001
76
+ +--->|rg_a|#{p_a}----->| | | #{loc == 2 ? ">" : " "} JNC 0001
77
+ | |#{a}| | | | #{loc == 3 ? ">" : " "} ADD A,0001
78
+ | | | | #{loc == 4 ? ">" : " "} JNC 0011
79
+ +--->|rg_b|#{p_b}----->| |----------->| |---+ #{loc == 5 ? ">" : " "} OUT 0110
80
+ | |#{b}| | | | | #{loc == 6 ? ">" : " "} ADD A,0001
81
+ | |SEL| |ALU| #{loc == 7 ? ">" : " "} JNC 0110
82
+ +--->| out| | in|--->| | | | #{loc == 8 ? ">" : " "} ADD A,0001
83
+ | |#{o}| |#{i}| | | | im|--->| |-#{c} #{loc == 9 ? ">" : " "} JNC 1000
84
+ | | | |#{m}| #{loc == 10 ? ">" : " "} OUT 0000
85
+ +--->| pc| (0000)--->| | #{loc == 11 ? ">" : " "} OUT 0100
86
+ |#{p}| #{loc == 12 ? ">" : " "} ADD A,0001
87
+ #{loc == 13 ? ">" : " "} JNC 1010
88
+ #{loc == 14 ? ">" : " "} OUT 1000
89
+ #{loc == 15 ? ">" : " "} JMP 1111
90
+ OUTPUT
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Logicuit
4
+ module Circuits
5
+ module Td4
6
+ # Decoder class
7
+ class Decoder < DSL
8
+ inputs :op3, :op2, :op1, :op0, :c_flag
9
+
10
+ outputs sel_b: -> { op1 },
11
+ sel_a: -> { op3 || op0 },
12
+ ld0: -> { op3 || op2 },
13
+ ld1: -> { op3 || !op2 },
14
+ ld2: -> { !op3 || op2 },
15
+ ld3: -> { !op3 || !op2 || (!op0 && c_flag) }
16
+
17
+ truth_table <<~TRUTH_TABLE
18
+ | OP3 | OP2 | OP1 | OP0 | C_FLAG | SEL_B | SEL_A | LD0 | LD1 | LD2 | LD3 |
19
+ | --- | --- | --- | --- | ------ | ----- | ----- | --- | --- | --- | --- |
20
+ | 0 | 0 | 0 | 0 | x | 0 | 0 | 0 | 1 | 1 | 1 | # ADD A,Im
21
+ | 0 | 0 | 0 | 1 | x | 0 | 1 | 0 | 1 | 1 | 1 | # MOV A,B
22
+ | 0 | 0 | 1 | 0 | x | 1 | 0 | 0 | 1 | 1 | 1 | # IN A
23
+ | 0 | 0 | 1 | 1 | x | 1 | 1 | 0 | 1 | 1 | 1 | # MOV A,Im
24
+ | 0 | 1 | 0 | 0 | x | 0 | 0 | 1 | 0 | 1 | 1 | # MOV B,A
25
+ | 0 | 1 | 0 | 1 | x | 0 | 1 | 1 | 0 | 1 | 1 | # ADD B,Im
26
+ | 0 | 1 | 1 | 0 | x | 1 | 0 | 1 | 0 | 1 | 1 | # IN B
27
+ | 0 | 1 | 1 | 1 | x | 1 | 1 | 1 | 0 | 1 | 1 | # MOV B,Im
28
+ | 1 | 0 | 0 | 1 | x | 0 | 1 | 1 | 1 | 0 | 1 | # OUT B
29
+ | 1 | 0 | 1 | 1 | x | 1 | 1 | 1 | 1 | 0 | 1 | # OUT Im
30
+ | 1 | 1 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 0 | # JNC(C=0)
31
+ | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | # JNC(C=1)
32
+ | 1 | 1 | 1 | 1 | x | 1 | 1 | 1 | 1 | 1 | 0 | # JMP
33
+ TRUTH_TABLE
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Logicuit
4
+ module Circuits
5
+ module Td4
6
+ # Timer
7
+ class Rom < DSL
8
+ inputs :a3, :a2, :a1, :a0
9
+
10
+ outputs :d7, :d6, :d5, :d4, :d3, :d2, :d1, :d0
11
+
12
+ def evaluate
13
+ return unless initialized
14
+
15
+ output = case "#{a3}#{a2}#{a1}#{a0}"
16
+ in "0000" then "10110111"
17
+ in "0001" then "00000001"
18
+ in "0010" then "11100001"
19
+ in "0011" then "00000001"
20
+ in "0100" then "11100011"
21
+ in "0101" then "10110110"
22
+ in "0110" then "00000001"
23
+ in "0111" then "11100110"
24
+ in "1000" then "00000001"
25
+ in "1001" then "11101000"
26
+ in "1010" then "10110000"
27
+ in "1011" then "10110100"
28
+ in "1100" then "00000001"
29
+ in "1101" then "11101010"
30
+ in "1110" then "10111000"
31
+ in "1111" then "11111111"
32
+ end
33
+ output.split("").zip([d7, d6, d5, d4, d3, d2, d1, d0]).each do |v, o|
34
+ v == "1" ? o.on : o.off
35
+ end
36
+ end
37
+
38
+ truth_table <<~TRUTH_TABLE
39
+ | A3 | A2 | A1 | A0 | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
40
+ | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- |
41
+ | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 0 | 1 | 1 | 1 | # OUT 0111
42
+ | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | # ADD A,0001
43
+ | 0 | 0 | 1 | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 1 | # JNC 0001
44
+ | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | # ADD A,0001
45
+ | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 1 | 1 | # JNC 0011
46
+ | 0 | 1 | 0 | 1 | 1 | 0 | 1 | 1 | 0 | 1 | 1 | 0 | # OUT 0110
47
+ | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | # ADD A,0001
48
+ | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 1 | 1 | 0 | # JNC 0110
49
+ | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | # ADD A,0001
50
+ | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 0 | 1 | 0 | 0 | 0 | # JNC 1000
51
+ | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | # OUT 0000
52
+ | 1 | 0 | 1 | 1 | 1 | 0 | 1 | 1 | 0 | 1 | 0 | 0 | # OUT 0100
53
+ | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | # ADD A,0001
54
+ | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 1 | 0 | 1 | 0 | # JNC 1010
55
+ | 1 | 1 | 1 | 0 | 1 | 0 | 1 | 1 | 1 | 0 | 0 | 0 | # OUT 1000
56
+ | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | # JMP 1111
57
+ TRUTH_TABLE
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,158 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Logicuit module
4
+ module Logicuit
5
+ # base class for all gates and circuits
6
+ class DSL
7
+ def initialize(*args)
8
+ @input_targets = []
9
+ @inputs_as_bool_struct = nil
10
+ @output_targets = []
11
+ @clock = false
12
+ @components = []
13
+ inputs(*args)
14
+ outputs
15
+ assembling
16
+ @initialized = true
17
+ evaluate
18
+ end
19
+
20
+ def inputs(*args); end
21
+ def outputs; end
22
+ def assembling; end
23
+ def evaluate(*args); end
24
+
25
+ attr_reader :input_targets, :output_targets, :clock, :components, :initialized
26
+
27
+ def self.inputs(*args, **kwargs)
28
+ # define getter methods for inputs
29
+ attr_reader(*args)
30
+
31
+ # define initializer for inputs
32
+ define_method(:inputs) do |*instance_method_args|
33
+ @clock = true if kwargs&.key?(:clock)
34
+ args.each_with_index do |input, index|
35
+ signal = Signals::Signal.new(instance_method_args[index] == 1)
36
+ signal >> self unless clock
37
+ instance_variable_set("@#{input}", signal)
38
+ @input_targets << input
39
+ end
40
+ Signals::Clock >> self if clock
41
+ @inputs_as_bool_struct = Struct.new(*@input_targets)
42
+ end
43
+ end
44
+
45
+ def [](*keys)
46
+ if keys.size == 1
47
+ send(keys.first)
48
+ elsif keys.size > 1
49
+ Signals::SignalGroup.new(*(keys.map { |key| send(key) }))
50
+ else
51
+ raise ArgumentError, "Invalid number of arguments"
52
+ end
53
+ end
54
+
55
+ def self.outputs(*args, **kwargs)
56
+ # define getter methods for outputs
57
+ attr_reader(*(args + kwargs.keys))
58
+
59
+ # define initializer for outputs
60
+ define_method(:outputs) do
61
+ (args + kwargs.keys).each do |output|
62
+ instance_variable_set("@#{output}", Signals::Signal.new(false))
63
+ @output_targets << output
64
+ end
65
+ end
66
+
67
+ # define evaluate method
68
+ return if kwargs.empty?
69
+
70
+ define_method(:evaluate) do |*override_args|
71
+ return unless initialized
72
+
73
+ kwargs.each do |output, evaluator|
74
+ signal = instance_variable_get("@#{output}")
75
+ e_args = if override_args.empty?
76
+ @input_targets.map do |input|
77
+ instance_variable_get("@#{input}").current
78
+ end
79
+ else
80
+ override_args
81
+ end
82
+ if @inputs_as_bool_struct.new(*e_args).instance_exec(&evaluator)
83
+ signal.on
84
+ else
85
+ signal.off
86
+ end
87
+ end
88
+ end
89
+ end
90
+
91
+ def self.assembling(&block)
92
+ define_method(:assembling) do
93
+ ret = instance_eval(&block)
94
+ ret.each { @components << it } if ret.is_a?(Array)
95
+ end
96
+ end
97
+
98
+ def self.diagram(source)
99
+ define_method(:to_s) do
100
+ source_ = @input_targets.reduce(source) do |result, input|
101
+ result.gsub(/\(#{input}\)/i, "(#{instance_variable_get("@#{input}")})#{"-" * (input.size - 1)}")
102
+ end
103
+ @output_targets.reduce(source_) do |result, output|
104
+ result.gsub(/\(#{output}\)/i, "#{"-" * (output.size - 1)}(#{instance_variable_get("@#{output}")})")
105
+ end
106
+ end
107
+ end
108
+
109
+ def self.truth_table(source)
110
+ define_method(:truth_table) do
111
+ rows = source.strip.split("\n").map { |row| row.gsub(/#.*$/, "") }
112
+ headers = rows.shift.split("|").map(&:strip).reject(&:empty?).map(&:downcase).map(&:to_sym)
113
+ rows.shift # devide line
114
+ table = rows.map do |row|
115
+ row.split("|").map(&:strip).reject(&:empty?).map(&:downcase).map do |v|
116
+ case v
117
+ when "^"
118
+ :clock
119
+ when "x"
120
+ :any
121
+ when "1"
122
+ true
123
+ when "0"
124
+ false
125
+ else
126
+ raise "Invalid value in truth table: #{v}" unless headers.include?(v.to_sym)
127
+
128
+ [:ref, v.to_sym]
129
+ end
130
+ end
131
+ end.select do |values|
132
+ headers.size == values.size
133
+ end.map do |values|
134
+ array = [values]
135
+ while array.any? { it.any? { |v| v == :any } }
136
+ target_index = array.find_index { it.any? { |v| v == :any } }
137
+ target = array[target_index]
138
+ prop_index = target.find_index { |v| v == :any }
139
+ array.delete_at(target_index)
140
+ array.insert(target_index, *[true, false].map do |v|
141
+ target.dup.tap do |a|
142
+ a[prop_index] = v
143
+ end
144
+ end)
145
+ end
146
+ array
147
+ end.flatten!(1).map do |values|
148
+ headers.zip(values).to_h
149
+ end
150
+ table
151
+ end
152
+ end
153
+
154
+ def self.run(opts = {})
155
+ ::Logicuit.run(new, **opts)
156
+ end
157
+ end
158
+ end
@@ -3,18 +3,16 @@
3
3
  module Logicuit
4
4
  module Gates
5
5
  # AND gate
6
- class And < Base
7
- tag :AND
8
-
6
+ class And < DSL
9
7
  diagram <<~DIAGRAM
10
- (A)-|
8
+ (A)-| |
11
9
  |AND|-(Y)
12
- (B)-|
10
+ (B)-| |
13
11
  DIAGRAM
14
12
 
15
- define_inputs :a, :b
13
+ inputs :a, :b
16
14
 
17
- define_outputs y: ->(a, b) { a && b }
15
+ outputs y: -> { a && b }
18
16
 
19
17
  truth_table <<~TRUTH_TABLE
20
18
  | A | B | Y |
@@ -3,18 +3,16 @@
3
3
  module Logicuit
4
4
  module Gates
5
5
  # NAND gate
6
- class Nand < Base
7
- tag :NAND
8
-
6
+ class Nand < DSL
9
7
  diagram <<~DIAGRAM
10
- (A)-|
8
+ (A)-| |
11
9
  |NAND|-(Y)
12
- (B)-|
10
+ (B)-| |
13
11
  DIAGRAM
14
12
 
15
- define_inputs :a, :b
13
+ inputs :a, :b
16
14
 
17
- define_outputs y: ->(a, b) { !(a && b) }
15
+ outputs y: -> { !(a && b) }
18
16
 
19
17
  truth_table <<~TRUTH_TABLE
20
18
  | A | B | Y |
@@ -3,16 +3,14 @@
3
3
  module Logicuit
4
4
  module Gates
5
5
  # NOT gate
6
- class Not < Base
7
- tag :NOT
8
-
6
+ class Not < DSL
9
7
  diagram <<~DIAGRAM
10
8
  (A)-|NOT|-(Y)
11
9
  DIAGRAM
12
10
 
13
- define_inputs :a
11
+ inputs :a
14
12
 
15
- define_outputs y: ->(a) { !a } # rubocop:disable Style/SymbolProc
13
+ outputs y: -> { !a }
16
14
 
17
15
  truth_table <<~TRUTH_TABLE
18
16
  | A | Y |
@@ -3,18 +3,16 @@
3
3
  module Logicuit
4
4
  module Gates
5
5
  # OR gate
6
- class Or < Base
7
- tag :OR
8
-
6
+ class Or < DSL
9
7
  diagram <<~DIAGRAM
10
- (A)-|
8
+ (A)-| |
11
9
  |OR|-(Y)
12
- (B)-|
10
+ (B)-| |
13
11
  DIAGRAM
14
12
 
15
- define_inputs :a, :b
13
+ inputs :a, :b
16
14
 
17
- define_outputs y: ->(a, b) { a || b }
15
+ outputs y: -> { a || b }
18
16
 
19
17
  truth_table <<~TRUTH_TABLE
20
18
  | A | B | Y |
@@ -3,18 +3,16 @@
3
3
  module Logicuit
4
4
  module Gates
5
5
  # XOR gate
6
- class Xor < Base
7
- tag :XOR
8
-
6
+ class Xor < DSL
9
7
  diagram <<~DIAGRAM
10
- (A)-|
8
+ (A)-| |
11
9
  |XOR|-(Y)
12
- (B)-|
10
+ (B)-| |
13
11
  DIAGRAM
14
12
 
15
- define_inputs :a, :b
13
+ inputs :a, :b
16
14
 
17
- define_outputs y: ->(a, b) { (a && !b) || (!a && b) }
15
+ outputs y: -> { (a && !b) || (!a && b) }
18
16
 
19
17
  truth_table <<~TRUTH_TABLE
20
18
  | A | B | Y |
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Logicuit module
4
+ module Logicuit
5
+ def self.run(circuit, hz: 1, noclear: false)
6
+ render = lambda {
7
+ system("clear") unless noclear
8
+ puts circuit
9
+ puts
10
+ puts "tick: #{Signals::Clock.tick_count}" if circuit.clock
11
+ puts "input: #{circuit.input_targets.join ","}?" if circuit.input_targets.any?
12
+ }
13
+
14
+ if circuit.clock && hz.nonzero?
15
+ Thread.new do
16
+ loop do
17
+ render.call
18
+ sleep 1.0 / hz
19
+ Signals::Clock.tick
20
+ end
21
+ end
22
+ else
23
+ render.call
24
+ end
25
+
26
+ while (input = gets.chomp)
27
+ key = input.to_sym
28
+ unless circuit.respond_to? key
29
+ if circuit.clock && hz.zero?
30
+ Signals::Clock.tick
31
+ render.call
32
+ end
33
+ next
34
+ end
35
+
36
+ signal = circuit.send(key)
37
+ if signal.current
38
+ signal.off
39
+ else
40
+ signal.on
41
+ end
42
+ render.call
43
+ end
44
+ end
45
+ end
@@ -5,31 +5,39 @@ module Logicuit
5
5
  # Clock
6
6
  class Clock
7
7
  def initialize
8
- @on_tick = []
8
+ @downstreams = []
9
9
  @tick_count = 0
10
10
  end
11
11
 
12
- attr_reader :on_tick, :tick_count
12
+ attr_reader :downstreams, :tick_count
13
13
 
14
14
  def tick
15
15
  @tick_count += 1
16
- @on_tick.each(&:evaluate)
16
+ # Call the `evaluate` method for all components.
17
+ # However, the input argument values should be bound to the values at the time `tick` is called.
18
+ @downstreams.map do |component|
19
+ args = component.input_targets.map { |input| component.instance_variable_get("@#{input}").current }
20
+ -> { component.evaluate(*args) }
21
+ end.each(&:call)
17
22
  end
18
23
 
19
- def self.instance
20
- @instance ||= new
21
- end
24
+ class << self
25
+ def instance
26
+ @instance ||= new
27
+ end
22
28
 
23
- def self.on_tick
24
- instance.on_tick
25
- end
29
+ def connects_to(component)
30
+ instance.downstreams << component
31
+ end
32
+ alias >> connects_to
26
33
 
27
- def self.tick
28
- instance.tick
29
- end
34
+ def tick
35
+ instance.tick
36
+ end
30
37
 
31
- def self.tick_count
32
- instance.tick_count
38
+ def tick_count
39
+ instance.tick_count
40
+ end
33
41
  end
34
42
  end
35
43
  end