Class: Tem::Builders::Isa
- Inherits:
-
Object
- Object
- Tem::Builders::Isa
- Defined in:
- lib/tem/builders/isa.rb
Overview
Builder class for the ISA (Instruction Set Architecture) builder.
Instance Attribute Summary collapse
-
#target ⇒ Object
readonly
The module / class impacted by the builder.
Class Method Summary collapse
-
.define_isa(class_or_module, abi, options) {|new(class_or_module, abi, options[:opcode_type])| ... } ⇒ Object
Creates a builder targeting a module / class.
Instance Method Summary collapse
-
#initialize(target, abi, opcode_type) ⇒ Isa
constructor
Creates a builder targeting a module / class.
-
#instruction(opcode, name, *iargs) ⇒ Object
Defines the methods for handling an instruction in the IA.
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
#target ⇒ Object (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)
23 24 25 |
# File 'lib/tem/builders/isa.rb', line 23 def self.define_isa(class_or_module, abi, ) # :yields: isa yield new(class_or_module, abi, [: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 |