clean up intruction instantiation and fix tests

This commit is contained in:
Torsten Ruger
2014-05-14 10:47:30 +03:00
parent 46ea1df51e
commit 2230a4f25e
16 changed files with 86 additions and 76 deletions

View File

@ -34,6 +34,7 @@ module Vm
end
def add_code(kode)
raise "alarm #{kode}" if kode.is_a? Word
@codes << kode
self
end

View File

@ -36,46 +36,53 @@ module Vm
# consistency in this code, but also because that is what is actually done
attr_reader :status
# conditions specify all the possibilities for branches. Branches are b + condition
# Example: beq means brach if equal.
# :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 ]
# here we create the shortcuts for the "standard" instructions, see above
# Derived machines may use own instructions and define functions for them if so desired
def initialize
[:push, :pop].each do |inst|
define_instruction(inst , StackInstruction)
define_instruction_for(inst , StackInstruction)
end
[:adc, :add, :and, :bic, :eor, :orr, :rsb, :rsc, :sbc, :sub].each do |inst|
define_instruction(inst , LogicInstruction)
define_instruction_for(inst , LogicInstruction)
end
[:mov, :mvn].each do |inst|
define_instruction(inst , MoveInstruction)
define_instruction_for(inst , MoveInstruction)
end
[:cmn, :cmp, :teq, :tst].each do |inst|
define_instruction(inst , CompareInstruction)
define_instruction_for(inst , CompareInstruction)
end
[:strb, :str , :ldrb, :ldr].each do |inst|
define_instruction(inst , MemoryInstruction)
define_instruction_for(inst , MemoryInstruction)
end
[:b, :bl , :swi].each do |inst|
define_instruction(inst , CallInstruction)
[:b, :call , :swi].each do |inst|
define_instruction_for(inst , CallInstruction)
end
# create all possible brach instructions, but the CallInstruction demangles the
# code, and has opcode set to :b and :condition_code set to the condition
CONDITIONS.each do |suffix|
define_instruction_for("b#{suffix}".to_sym , CallInstruction)
end
end
def create_method(name, &block)
self.class.send(:define_method, name , &block)
end
def define_instruction(inst , clazz )
c_name = clazz.name
my_module = self.class.name.split("::").first
clazz_name = clazz.name.split("::").last
if(my_module != Vm )
module_class = eval("#{my_module}::#{clazz_name}") rescue nil
clazz = module_class if module_class
end
# define the instruction inst (given as a symbol) on this class as a methods
# As we define a standard set of instructions (or memnonics) , this turns this class into a kind of
# Assembler, in that you can write .mov() or .pop() and those functions mean the same as if they
# were in an assembler file (also options are the same)
# defaults gets merged into the instructions options hash, ie passed on to the (machine specific)
# Instruction constructor and as such can be used to influence that classes behaviour
def define_instruction(inst , clazz , defaults = {} )
create_method(inst) do |options|
options = {} if options == nil
options.merge defaults
options[:opcode] = inst
clazz.new(options)
end
@ -87,5 +94,25 @@ module Vm
def self.instance= machine
@@instance = machine
end
private
#defining the instruction (opcode, symbol) as an given class.
# the class is a Vm::Instruction derived base class and to create machine specific function
# an actual machine must create derived classes (from this base class)
# These instruction classes must follow a naming pattern and take a hash in the contructor
# Example, a mov() opcode instantiates a Vm::MoveInstruction
# for an Arm machine, a class Arm::MoveInstruction < Vm::MoveInstruction exists, and it will
# be used to define the mov on an arm machine.
# This methods picks up that derived class and calls a define_instruction methods that can
# be overriden in subclasses
def define_instruction_for(inst , clazz )
c_name = clazz.name
my_module = self.class.name.split("::").first
clazz_name = clazz.name.split("::").last
if(my_module != Vm )
module_class = eval("#{my_module}::#{clazz_name}") rescue nil
clazz = module_class if module_class
end
define_instruction(inst , clazz )
end
end
end

View File

@ -43,5 +43,13 @@ module Vm
class MoveInstruction < Instruction
end
class CallInstruction < Instruction
def initialize options
super
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
end
end
end