require 'asm/assembler'

module Asm
  module Arm
    
    # Relocation constants
    # Note that in this assembler, a relocation simply means any
    # reference to a label that can only be determined at assembly time
    # or later (as in the normal meaning)
  
    R_ARM_PC24 = 0x01
    R_ARM_ABS32 = 0x02
  
    # Unofficial (cant be used for extern relocations)
    R_ARM_PC12 = 0xF0
  
    # TODO actually find the closest somehow
    def self.closest_addrtable(as)
      as.objects.find do |obj|
        obj.is_a?(Asm::Arm::AddrTableObject)
      end || (raise Asm::AssemblyError.new('could not find addrtable to use', nil))
    end

    def self.write_resolved_relocation(io, addr, type)
      case type
      when R_ARM_PC24
        diff = addr - io.tell - 8
        packed = [diff >> 2].pack('l')
        io << packed[0,3]
      when R_ARM_ABS32
        packed = [addr].pack('l')
        io << packed
      when R_ARM_PC12
        diff = addr - io.tell - 8
        if (diff.abs > 2047)
          raise Asm::AssemblyError.new('offset too large for R_ARM_PC12 relocation',
                                      nil)
        end
      
        val = diff.abs
        sign = (diff>0)?1:0
      
        curr = io.read_uint32
        io.seek(-4, IO::SEEK_CUR)
      
        io.write_uint32 (curr & ~0b00000000100000000000111111111111) | 
                        val | (sign << 23)
      else
        raise 'unknown relocation type'
      end
    end
  end
end