2016-12-14 12:43:13 +01:00
|
|
|
require_relative "attributed"
|
|
|
|
|
|
|
|
module Arm
|
|
|
|
|
2018-03-11 11:41:15 +01:00
|
|
|
# A Machines main responsibility is to instantiate Instructions.
|
|
|
|
# Arm instructions live in their own directory and are derived from their Risc
|
|
|
|
# couterparts to inherit list functionality
|
2016-12-14 12:43:13 +01:00
|
|
|
|
2018-03-11 11:41:15 +01:00
|
|
|
# Shortcuts are created to easily instantiate Instruction objects.
|
|
|
|
# Example: ArmMachine.pop -> StackInstruction.new( {:opcode => :pop}.merge(options) )
|
|
|
|
#
|
2016-12-14 12:43:13 +01:00
|
|
|
# Instructions work with options, so you can pass anything in, and the only thing the functions
|
|
|
|
# does is save you typing the clazz.new. It passes the function name as the :opcode
|
|
|
|
|
|
|
|
class ArmMachine
|
|
|
|
|
2018-03-11 11:41:15 +01:00
|
|
|
# conditions specify all the possibilities for branches. Branches are b + condition
|
2018-03-19 16:48:56 +01:00
|
|
|
# Example: beq means branch if equal.
|
2016-12-14 12:43:13 +01:00
|
|
|
# :al means always, so bal is an unconditional branch (but b() also works)
|
|
|
|
CONDITIONS = [:al ,:eq ,:ne ,:lt ,:le ,:ge,:gt ,:cs ,:mi ,:hi ,:cc ,:pl,:ls ,:vc ,:vs]
|
|
|
|
|
2018-03-11 11:41:15 +01:00
|
|
|
# here we create the shortcuts for the "standard" arm instructions that we use.
|
|
|
|
# (note that it is possible to add instructions by adding new classes and optionally
|
|
|
|
# new factory functions to this class)
|
2016-12-14 12:43:13 +01:00
|
|
|
def self.init
|
|
|
|
[:push, :pop].each do |inst|
|
|
|
|
define_instruction_one(inst , StackInstruction)
|
|
|
|
end
|
|
|
|
[:adc, :add, :and, :bic, :eor, :orr, :rsb, :rsc, :sbc, :sub , :mul].each do |inst|
|
|
|
|
define_instruction_three(inst , LogicInstruction)
|
|
|
|
end
|
|
|
|
[:mov, :mvn].each do |inst|
|
|
|
|
define_instruction_two(inst , MoveInstruction)
|
|
|
|
end
|
|
|
|
[:cmn, :cmp, :teq, :tst].each do |inst|
|
|
|
|
define_instruction_two(inst , CompareInstruction)
|
|
|
|
end
|
|
|
|
[:strb, :str , :ldrb, :ldr].each do |inst|
|
|
|
|
define_instruction_three(inst , MemoryInstruction)
|
|
|
|
end
|
|
|
|
[:b, :call , :swi].each do |inst|
|
|
|
|
define_instruction_one(inst , CallInstruction)
|
|
|
|
end
|
2018-03-11 11:41:15 +01:00
|
|
|
|
|
|
|
# create all possible branch instructions, but the CallInstruction demangles the
|
2016-12-14 12:43:13 +01:00
|
|
|
# code, and has opcode set to :b and :condition_code set to the condition
|
|
|
|
CONDITIONS.each do |suffix|
|
|
|
|
define_instruction_one("b#{suffix}".to_sym , CallInstruction)
|
|
|
|
define_instruction_one("call#{suffix}".to_sym , CallInstruction)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-12-16 23:21:12 +01:00
|
|
|
def self.def_method(name, &block)
|
2016-12-14 12:43:13 +01:00
|
|
|
self.class.send(:define_method, name , &block)
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.class_for clazz
|
|
|
|
my_module = self.class.name.split("::").first
|
|
|
|
clazz_name = clazz.name.split("::").last
|
2017-01-19 08:02:29 +01:00
|
|
|
if(my_module != Risc )
|
2016-12-14 12:43:13 +01:00
|
|
|
module_class = eval("#{my_module}::#{clazz_name}") rescue nil
|
|
|
|
clazz = module_class if module_class
|
|
|
|
end
|
|
|
|
clazz
|
|
|
|
end
|
|
|
|
|
|
|
|
#defining the instruction (opcode, symbol) as an given class.
|
2017-01-19 08:02:29 +01:00
|
|
|
# the class is a Risc::Instruction derived base class and to create machine specific function
|
2018-03-11 11:41:15 +01:00
|
|
|
#
|
2016-12-14 12:43:13 +01:00
|
|
|
# These instruction classes must follow a naming pattern and take a hash in the contructor
|
2018-03-11 11:41:15 +01:00
|
|
|
# Example, a mov() opcode instantiates a Arm::MoveInstruction < Risc::MoveInstruction ,
|
|
|
|
#
|
2016-12-14 12:43:13 +01:00
|
|
|
def self.define_instruction_one(inst , clazz , defaults = {} )
|
|
|
|
clazz = class_for(clazz)
|
2016-12-16 23:21:12 +01:00
|
|
|
def_method(inst) do |first , options = nil|
|
2016-12-14 12:43:13 +01:00
|
|
|
options = {} if options == nil
|
|
|
|
options.merge defaults
|
|
|
|
options[:opcode] = inst
|
|
|
|
clazz.new(first , options)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# same for two args (left right, from to etc)
|
|
|
|
def self.define_instruction_two(inst , clazz , defaults = {} )
|
|
|
|
clazz = self.class_for(clazz)
|
2016-12-16 23:21:12 +01:00
|
|
|
def_method(inst) do |left ,right , options = nil|
|
2016-12-14 12:43:13 +01:00
|
|
|
options = {} if options == nil
|
|
|
|
options.merge defaults
|
|
|
|
options[:opcode] = inst
|
|
|
|
clazz.new(left , right ,options)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# same for three args (result = left right,)
|
|
|
|
def self.define_instruction_three(inst , clazz , defaults = {} )
|
|
|
|
clazz = self.class_for(clazz)
|
2016-12-16 23:21:12 +01:00
|
|
|
def_method(inst) do |result , left ,right = nil , options = nil|
|
2016-12-14 12:43:13 +01:00
|
|
|
options = {} if options == nil
|
|
|
|
options.merge defaults
|
|
|
|
options[:opcode] = inst
|
|
|
|
clazz.new(result, left , right ,options)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
Arm::ArmMachine.init
|