Class: FAM::Machine::CPU

Inherits:
Object show all
Includes:
Syntax
Defined in:
lib/fam/machine/cpu.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(ram) ⇒ CPU

Returns a new instance of CPU.



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# File 'lib/fam/machine/cpu.rb', line 11

def initialize ram
  @ram = ram
  @registers = {
    :ACC => 0,
    :DAT => 0,
    :PC  => 0
  }
  @memory_aliases = Hash.new

  @labels = Hash.new
  @last_jump = nil
  @back_index = 0
  @tree_index = -1
  @halted = false
  @outputting = @inputting = false
  @output = String.new
end

Instance Attribute Details

#haltedObject (readonly)

Returns the value of attribute halted.



8
9
10
# File 'lib/fam/machine/cpu.rb', line 8

def halted
  @halted
end

#inputtingObject (readonly)

Returns the value of attribute inputting.



8
9
10
# File 'lib/fam/machine/cpu.rb', line 8

def inputting
  @inputting
end

#labelsObject (readonly)

Returns the value of attribute labels.



7
8
9
# File 'lib/fam/machine/cpu.rb', line 7

def labels
  @labels
end

#memory_aliasesObject (readonly)

Returns the value of attribute memory_aliases.



7
8
9
# File 'lib/fam/machine/cpu.rb', line 7

def memory_aliases
  @memory_aliases
end

#outputObject (readonly)

Returns the value of attribute output.



9
10
11
# File 'lib/fam/machine/cpu.rb', line 9

def output
  @output
end

#outputtingObject (readonly)

Returns the value of attribute outputting.



8
9
10
# File 'lib/fam/machine/cpu.rb', line 8

def outputting
  @outputting
end

#ramObject (readonly)

Returns the value of attribute ram.



7
8
9
# File 'lib/fam/machine/cpu.rb', line 7

def ram
  @ram
end

#registersObject (readonly)

Returns the value of attribute registers.



7
8
9
# File 'lib/fam/machine/cpu.rb', line 7

def registers
  @registers
end

Class Method Details

.quick_run(ram, parsed, &block) ⇒ Object



29
30
31
32
33
34
35
36
37
# File 'lib/fam/machine/cpu.rb', line 29

def self.quick_run ram, parsed, &block
  cpu = self.new ram
  if block_given?
    cpu.run parsed, &block
  else
    cpu.run parsed
  end
  cpu
end

Instance Method Details

#cpu_inputObject

Ment to be changed



160
161
162
163
# File 'lib/fam/machine/cpu.rb', line 160

def cpu_input  # Ment to be changed
  print 'INPUT: '
  $stdin.gets.to_i
end

#cpu_output(out, ascii = :PLAIN) ⇒ Object

Ment to be changed



165
166
167
168
# File 'lib/fam/machine/cpu.rb', line 165

def cpu_output out, ascii=:PLAIN  # Ment to be changed
  show = ascii == :PLAIN ? out.to_s : out.chr
  puts "OUTPUT: #{show}"
end

#displayObject



151
152
153
154
155
156
157
158
# File 'lib/fam/machine/cpu.rb', line 151

def display
  puts "#{@ram.to_s}"
  puts
  @registers.each { |key, value| print "#{key} => #{value}\n" }
  puts
  # print "\033[F" * (self.to_s.count("\n"))
  # print "\n" * (@ram.to_s.count("\n") + 1)
end

#execute(node) ⇒ Object



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/fam/machine/cpu.rb', line 69

def execute node
  @outputting = @inputting = false
  @registers[:PC] = @tree_index
  @block.call @registers[:PC] unless @block.nil?
  case node
  when AST::LabelNode
    @last_jump = node.label
    @labels[node.label] = @tree_index
    @back_index = @tree_index
  when AST::HaltNode
    return :STOP
  when AST::GotoNode
    back = @tree_index
    if @labels.include? node.ident.name
      @tree_index = @labels[node.ident.name]
    elsif node.ident.name == '_BACK'
      @tree_index = @back_index
      return :PASS
    else  # Search ahead
      found = false
      @parsed.tree.each.with_index do |top_node, i|
        @tree_index = i
        if top_node.base_name == 'LabelNode'
          (found = true; break) if top_node.label == node.ident.name
        end
      end
      abort "Label: `#{node.label.name}` was not found. Execution HALTED!".red.bold unless found
    end
    @last_jump = node.ident.name
    @back_index = back
  when AST::StoreNode
    mem_index = case node.to
    when AST::AddressNode
      node.to.address
    when AST::IdentNode
      @memory_aliases[node.to.name]
    end
    @ram[mem_index] = get_value node, node.value
  when AST::LoadNode
    @registers[node.register.register.to_sym] = get_value node, node.value
  when AST::DataNode
    initial_value = get_value node, node.initial
    free_index = nil
    @ram.array.each.with_index { |value, i| if value == NULL then free_index = i; break; end }
    abort ("No free memory, allocation of `#{node.name.name}`,\n" +
    "cannot happen! Execution HALTED!").red.bold if free_index.nil?
    @memory_aliases[node.name.name] = free_index
    @ram[free_index] = initial_value
  when AST::InNode
    @registers[node.register.register.to_sym] = cpu_input
  when AST::OutNode, AST::AsciiNode
    @outputting = true
    value = get_value node, node.value
    @output = cpu_output(value, (node.base_name == 'OutNode' ? :PLAIN : :ASCII))
  when AST::SubNode, AST::AddNode, AST::MulNode, AST::DivNode, AST::ModNode
    value = get_value node, node.value
    ExpectedNode 'REGISTER', node.to unless node.to.base_name == 'RegisterNode'
    if node.base_name[/Sub|Add/]
      @registers[node.to.register.to_sym] += value * (node.base_name == 'AddNode' ? 1 : -1)
    elsif node.base_name == 'Mul'
      @registers[node.to.register.to_sym] *= value
    elsif node.base_name == 'Div'
      @registers[node.to.register.to_sym] /= value
    else
      @registers[node.to.register.to_sym] %= value
    end
  when AST::EqualNode, AST::MoreNode, AST::LessNode
    left  = get_value node, node.left
    right = get_value node, node.right
    if node.base_name == 'EqualNode' ? (left == right) : (node.base_name == 'MoreNode' ? (left > right) : (left < right))
      execute node.valid
    else
      execute node.invalid
    end
  else
    print "\n" * (@ram.to_s.count("\n") + 1) if $VERBOSE
    abort ("Unknown Node: `#{node.base_name}`,\nparser generated an " +
    "unexecutable/unknown node... Execution HALTED!").red.bold
  end
  return :PASS
end

#ExpectedNode(expect, got) ⇒ Object



190
191
192
193
# File 'lib/fam/machine/cpu.rb', line 190

def ExpectedNode expect, got
  print "\n" * (@ram.to_s.count("\n") + 1) if $VERBOSE
  abort ("Expected: `#{expect}`, but insted got: `#{got}`").red.bold
end

#run(parsed, &block) ⇒ Object



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/fam/machine/cpu.rb', line 39

def run parsed, &block
  @block = block if block_given?
  @parsed = parsed

  while @tree_index < parsed.tree.size
    @tree_index += 1

    sleep 1.0 / $CLOCK
    node = parsed[@tree_index]
    status = execute node
    break if status == :STOP
    next  if status == :SKIP
    puts "\n  _BACK: #{parsed[@back_index].inspect}\n " if $VERBOSE
    display if $VERBOSE
  end
end

#step(parsed) ⇒ Object



56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/fam/machine/cpu.rb', line 56

def step parsed
  @parsed = parsed
  return {:state => 'done'} if @tree_index >= parsed.tree.size || @halted
  @tree_index += 1
  node = parsed[@tree_index]
  status = execute node
  if status == :STOP
    @halted = true
    return {:state => 'done'}
  end
  {:state => 'running', :input => @inputting, :output => @outputting}
end

#UnexpectedNode(node, sub_node) ⇒ Object



185
186
187
188
# File 'lib/fam/machine/cpu.rb', line 185

def UnexpectedNode node, sub_node
  print "\n" * (@ram.to_s.count("\n") + 1) if $VERBOSE
  abort ("Unexpected Node, you've given `#{sub_node.base_name}` AST::to Node: `#{node.base_name}`?").red.bold
end