Class: Tem::Builders::Isa

Inherits:
Object
  • Object
show all
Defined in:
lib/tem/builders/isa.rb

Overview

Builder class for the ISA (Instruction Set Architecture) builder.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(target, abi, opcode_type) ⇒ Isa

Creates a builder targeting a module / class.



117
118
119
120
121
122
# File 'lib/tem/builders/isa.rb', line 117

def initialize(target, abi, opcode_type)
  @target = target
  @target.const_set :Abi, abi
  @abi = abi
  @opcode_type = opcode_type
end

Instance Attribute Details

#targetObject (readonly)

The module / class impacted by the builder.



114
115
116
# File 'lib/tem/builders/isa.rb', line 114

def target
  @target
end

Class Method Details

.define_isa(class_or_module, abi, options) {|new(class_or_module, abi, options[:opcode_type])| ... } ⇒ Object

Creates a builder targeting a module / class.

class_or_module will receive the ISA method definitions. abi should be a class or module containing the ABI definitions that the ISA definitions refer to.

The following options are supported:

opcode_type

the ABI type encoding the instructions' opcodes (required)

Yields:

  • (new(class_or_module, abi, options[:opcode_type]))


23
24
25
# File 'lib/tem/builders/isa.rb', line 23

def self.define_isa(class_or_module, abi, options)  # :yields: isa
  yield new(class_or_module, abi, options[:opcode_type])
end

Instance Method Details

#instruction(opcode, name, *iargs) ⇒ Object

Defines the methods for handling an instruction in the IA.

The instruction's arguments are provided as an array of hashes. Each hash describes one argument, and the ordering in the array reflects the encoding order. The following hash keys are supported:

name

if defined, the argument can be provided as a named argument; named arguments must follow positional arguments

type

the ABI type encoding the argument (required)

reladdr

if defined, the encoded argument value is relative to the address of the instruction containing the argument;

The result of encoding an instruction is a Hash with the following keys:

emit

the bytes to be emitted into the code stream

link_directives

an array of directives for the code linker

Each linker directive refers to an address cell (location in the code representing an address that the linker must adjust. The following keys can be present:

type

the ABI type for the address cell (required)

offset

the address cell's offset in the emitted bytes (required)

address

the absolute address to point to (mutually exclusive with label)

label

the name of a label that the address must point to

relative

if false, the address cell holds an absolute address; otherwise, the cell's value is computed as follows: target address - cell address + value of relative; (optional, default value is false)

The following methods are defined for a type named 'name':

  • encode_name(*arguments) -> Hash


55
56
57
58
59
60
61
62
63
64
65
66
67
68
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
# File 'lib/tem/builders/isa.rb', line 55

def instruction(opcode, name, *iargs)
  encoded_opcode = @abi.send :"to_#{@opcode_type}", opcode
  abi = @abi  # Capture the ABI in the method closures.
  named_indexes = {}
  iargs.map { |iarg| iarg[:name] }.
        each_with_index { |argname, i| named_indexes[argname] = i if argname }
  arg_encode_msgs = iargs.map { |iarg| :"to_#{iarg[:type]}" }
  defines = Proc.new do 
    define_method :"emit_#{name}" do |*args|
      # Flatten arguments by resolving named parameters to indexes.
      arg_index = 0
      fargs = []
      args.each_with_index do |arg, i|
        fargs[i] = arg and next unless arg.kind_of? Hash
        
        if i != args.length - 1
          raise "Named arguments must follow inline arguments! (arg #{i})"
        end
        arg.each do |k, v|
          raise "#{name} has no #{k} argument" unless i = named_indexes[k]
          raise "Argument #{k} was already assigned a value" if fargs[i]
          fargs[i] = v
        end
      end
        
      arg_count = fargs.inject(0) { |acc, v| v.nil? ? acc : acc + 1 }
      if arg_count != iargs.length
        raise "#{name} requires #{fargs.length} args, given #{arg_count}"
      end
      
      # Encode parameters.
      # @lines[@body.length] = Kernel.caller(0)
      
      emit = encoded_opcode
      link_directives = []
      fargs.each_with_index do |arg, i|
        if arg.kind_of?(Numeric) && !iargs[i][:reladdr]
          emit += abi.send arg_encode_msgs[i], arg
        else
          link_directive = { :type => iargs[i][:type], :offset => emit.length,
                             :relative => iargs[i][:reladdr] || false }
          if arg.kind_of? Numeric
            link_directive[:address] = arg.to_i
          else
            link_directive[:label] = arg.to_sym
          end
          link_directives << link_directive
          emit += abi.send arg_encode_msgs[i], 0
        end
      end
      { :emit => emit, :link_directives => link_directives }
    end
  end
  
  @target.class_eval(&defines)
  (class << @target; self; end).module_eval(&defines)
end