diff --git a/lib/arm/instructions/logic_instruction.rb b/lib/arm/instructions/logic_instruction.rb index 18f98997..6dfceb55 100644 --- a/lib/arm/instructions/logic_instruction.rb +++ b/lib/arm/instructions/logic_instruction.rb @@ -3,7 +3,7 @@ module Arm include Constants include Attributed - # result = left op right + # result = left op right #or constant loading # # Logic instruction are your basic operator implementation. But unlike the (normal) code we write # these Instructions must have "place" to write their results. Ie when you write 4 + 5 in ruby @@ -16,55 +16,16 @@ module Arm @right = right @attributes[:update_status] = 1 if @attributes[:update_status] == nil @attributes[:condition_code] = :al if @attributes[:condition_code] == nil - @operand = 0 - raise "Left arg must be given #{inspect}" unless @left - @immediate = 0 end attr_accessor :result , :left , :right def assemble(io) - # don't overwrite instance variables, to make assembly repeatable - left = @left - operand = @operand - immediate = @immediate + left , right = determine_operands + immediate = 1 # default, unless register (below) - right = @right - if( @left.is_a?(Parfait::Object) or @left.is_a?(Register::Label) or - (@left.is_a?(Symbol) and !Register::RegisterValue.look_like_reg(@left))) - # do pc relative addressing with the difference to the instuction - # 8 is for the funny pipeline adjustment (ie pointing to fetch and not execute) - right = @left.position - self.position - 8 - if( (right < 0) && ((opcode == :add) || (opcode == :sub)) ) - right *= -1 # this works as we never issue sub only add - set_opcode :sub # so (as we can't change the sign permanently) we can change the opcode - end # and the sign even for sub (becuase we created them) - raise "No negatives implemented #{self} #{right} " if right < 0 - left = :pc - end if (right.is_a?(Numeric)) - if (right.fits_u8?) - # no shifting needed - operand = right - immediate = 1 - elsif (op_with_rot = calculate_u8_with_rr(right)) - operand = op_with_rot - immediate = 1 - else - #TODO this is copied from MoveInstruction, should rework - unless @extra - @extra = 1 - #puts "RELINK L at #{self.position.to_s(16)}" - raise ::Register::LinkException.new("cannot fit numeric literal argument in operand #{right.inspect}") - end - # now we can do the actual breaking of instruction, by splitting the operand - first = right & 0xFFFFFF00 - operand = calculate_u8_with_rr( first ) - raise "no fit for #{right}" unless operand - immediate = 1 - # use sub for sub and add for add, ie same as opcode - @extra = ArmMachine.send( opcode , result , result , (right & 0xFF) ) - end + operand = handle_numeric(right) elsif (right.is_a?(Symbol) or right.is_a?(::Register::RegisterValue)) operand = reg_code(right) #integer means the register the integer is in (otherwise constant) immediate = 0 # ie not immediate is register @@ -84,21 +45,67 @@ module Arm val = shift(operand , 0) val |= shift(op , 0) # any barrel action, is already shifted val |= shift(result , 12) - val |= shift(left_code , 12 + 4) + val |= shift(left_code , 12 + 4) val |= shift(@attributes[:update_status] , 12 + 4 + 4)#20 - val |= shift(op_bit_code , 12 + 4 + 4 + 1) - val |= shift(immediate , 12 + 4 + 4 + 1 + 4) - val |= shift(instuction_class , 12 + 4 + 4 + 1 + 4 + 1) - val |= shift(cond_bit_code , 12 + 4 + 4 + 1 + 4 + 1 + 2) + val |= shift(op_bit_code , 12 + 4 + 4 + 1) + val |= shift(immediate , 12 + 4 + 4 + 1 + 4) + val |= shift(instuction_class , 12 + 4 + 4 + 1 + 4 + 1) + val |= shift(cond_bit_code , 12 + 4 + 4 + 1 + 4 + 1 + 2) io.write_uint32 val - # by now we have the extra add so assemble that - if(@extra) - if(@extra == 1) # unles things have changed and then we add a noop (to keep the length same) - @extra = ArmMachine.mov( :r1 , :r1 ) + assemble_extra + end + + # Arm can't load any large (over 1024) numbers, or larger with fancy shifting, + # but then the lower bits must be 0's. Especially in constant loading random large numbers + # happen, and so they are split into two instructions. An exection is thrown, that triggers + # some position handling and an @extra add instruction generated. + def handle_numeric(right) + if (right.fits_u8?) + operand = right # no shifting needed + elsif (op_with_rot = calculate_u8_with_rr(right)) + operand = op_with_rot + else + unless @extra + @extra = 1 + #puts "RELINK L at #{self.position.to_s(16)}" + raise ::Register::LinkException.new("cannot fit numeric literal argument in operand #{right.inspect}") end - @extra.assemble(io) - #puts "Assemble extra at #{val.to_s(16)}" + # now we can do the actual breaking of instruction, by splitting the operand + operand = calculate_u8_with_rr( right & 0xFFFFFF00 ) + raise "no fit for #{right}" unless operand + # use sub for sub and add for add, ie same as opcode + @extra = ArmMachine.send( opcode , result , result , (right & 0xFF) ) end + return operand + end + + # don't overwrite instance variables, to make assembly repeatable + # this also loads constants, which are issued as pc relative adds + def determine_operands + if( @left.is_a?(Parfait::Object) or @left.is_a?(Register::Label) or + (@left.is_a?(Symbol) and !Register::RegisterValue.look_like_reg(@left))) + # do pc relative addressing with the difference to the instuction + # 8 is for the funny pipeline adjustment (ie pointing to fetch and not execute) + right = @left.position - self.position - 8 + if( (right < 0) && ((opcode == :add) || (opcode == :sub)) ) + right *= -1 # this works as we never issue sub only add + set_opcode :sub # so (as we can't change the sign permanently) we can change the opcode + end # and the sign even for sub (becuase we created them) + raise "No negatives implemented #{self} #{right} " if right < 0 + return :pc , right + else + return @left , @right + end + end + + # by now we have the extra add so assemble that + def assemble_extra + return unless @extra + if(@extra == 1) # unles things have changed and then we add a noop (to keep the length same) + @extra = ArmMachine.mov( :r1 , :r1 ) + end + @extra.assemble(io) + #puts "Assemble extra at #{val.to_s(16)}" end def byte_length