rubyx/lib/vm/instruction.rb

117 lines
3.5 KiB
Ruby
Raw Normal View History

2014-05-03 21:18:04 +02:00
require_relative "code"
require "support/hash_attributes"
2014-05-02 07:02:25 +02:00
module Vm
2014-05-03 14:13:44 +02:00
2014-05-03 21:18:04 +02:00
# Because the idea of what one instruction does, does not always map one to one to real machine
2014-05-03 14:13:44 +02:00
# instructions, and instruction may link to another instruction thus creating an arbitrary list
# to get the job (the original instruciton) done
2014-05-03 21:18:04 +02:00
2014-05-03 14:13:44 +02:00
# Admittately it would be simpler just to create the (abstract) instructions and let the machine
# encode them into what-ever is neccessary, but this approach leaves more possibility to
# optimize the actual instruction stream (not just the crystal instruction stream). Makes sense?
# We have basic classes (literally) of instructions
# - Memory
# - Stack
# - Logic
# - Math
# - Control/Compare
# - Move
# - Call
# Instruction derives from Code, for the assembly api
2014-05-03 21:18:04 +02:00
2014-05-19 10:28:13 +02:00
class Instruction < Code
def initialize options
2014-05-05 14:59:29 +02:00
@attributes = options
2014-05-03 21:18:04 +02:00
end
2014-05-19 10:28:13 +02:00
def opcode
@attributes[:opcode]
end
def method_missing name , *args , &block
return super unless (args.length <= 1) or block_given?
set , attribute = name.to_s.split("set_")
if set == ""
@attributes[attribute.to_sym] = args[0] || 1
return self
else
return super
end
return @attributes[name.to_sym]
end
2014-05-02 07:02:25 +02:00
end
2014-05-03 14:13:44 +02:00
class StackInstruction < Instruction
2014-05-19 10:28:13 +02:00
def initialize first , options = {}
@first = first
super(options)
end
2014-05-03 14:13:44 +02:00
end
class MemoryInstruction < Instruction
2014-05-19 10:28:13 +02:00
def initialize first , options = {}
@first = first
super(options)
end
2014-05-03 14:13:44 +02:00
end
class LogicInstruction < Instruction
# result = left op right
#
# Logic instruction are your basic operator implementation. But unlike the (normal) code we write
# these Instructions must have "place" to write their results. Ie when you write 4 + 5 in ruby
# the result is sort of up in the air, but with Instructions the result must be assigned
2014-05-19 10:28:13 +02:00
def initialize result , left , right , options = {}
2014-05-18 11:18:57 +02:00
@result = result
@left = left
@right = right
super(options)
end
# this is used to write code that looks like assignment
# So instructions can be created without the result (register) set, and this assigns where
# the reuslt after the fact, but usually in the same line
# Example (with block b, and variables int,a,b): b.int = a + b
# a + b actually creates an add instruction while the b.int= assigns the result to int
# b.add( int , a , b) is an alternative (assmbler style) way of writing the same.
def assign left
@result = left
self
end
2014-05-03 14:13:44 +02:00
end
class MathInstruction < Instruction
2014-05-19 10:28:13 +02:00
def initialize first , options = {}
@first = first
super(options)
end
2014-05-03 14:13:44 +02:00
end
class CompareInstruction < Instruction
2014-05-19 10:28:13 +02:00
def initialize left , right , options = {}
@left = left
@right = right
super(options)
end
2014-05-03 14:13:44 +02:00
end
class MoveInstruction < Instruction
2014-05-19 10:28:13 +02:00
def initialize to , from , options = {}
2014-05-18 11:30:49 +02:00
@to = to
@from = from
2014-05-19 10:28:13 +02:00
raise inspect unless from
super(options)
end
2014-05-03 14:13:44 +02:00
end
class CallInstruction < Instruction
2014-05-19 10:28:13 +02:00
def initialize first , options = {}
@first = first
super(options)
opcode = @attributes[:opcode].to_s
if opcode.length == 3 and opcode[0] == "b"
@attributes[:condition_code] = opcode[1,2].to_sym
@attributes[:opcode] = :b
end
if opcode.length == 6 and opcode[0] == "c"
@attributes[:condition_code] = opcode[4,2].to_sym
@attributes[:opcode] = :call
end
end
2014-05-03 14:13:44 +02:00
end
2014-05-02 07:02:25 +02:00
end