cleaned up BIG time, instruction hierachy, better names, DRYd up a lot
This commit is contained in:
parent
c98547137b
commit
f97205300f
@ -1,4 +1,7 @@
|
|||||||
require 'asm/instruction'
|
require 'asm/call_instruction'
|
||||||
|
require 'asm/stack_instruction'
|
||||||
|
require 'asm/logic_instruction'
|
||||||
|
require 'asm/memory_instruction'
|
||||||
require 'asm/nodes'
|
require 'asm/nodes'
|
||||||
require 'stream_reader'
|
require 'stream_reader'
|
||||||
require 'stringio'
|
require 'stringio'
|
||||||
@ -8,14 +11,9 @@ module Asm
|
|||||||
|
|
||||||
class ArmAssembler
|
class ArmAssembler
|
||||||
|
|
||||||
%w(r0 r1 r2 r3 r4 r5 r6 r7 r8 r9 r10 r11 r12
|
InstructionTools::REGISTERS.each do |reg , number|
|
||||||
r13 r14 r15 a1 a2 a3 a4 v1 v2 v3 v4 v5 v6
|
define_method(reg) { Asm::Register.new(reg , number) }
|
||||||
rfp sl fp ip sp lr pc
|
end
|
||||||
).each { |reg|
|
|
||||||
define_method(reg) {
|
|
||||||
Asm::Register.new(reg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def initialize
|
def initialize
|
||||||
@values = []
|
@values = []
|
||||||
@ -28,8 +26,7 @@ module Asm
|
|||||||
def instruction(clazz,name, *args)
|
def instruction(clazz,name, *args)
|
||||||
opcode = name.to_s
|
opcode = name.to_s
|
||||||
arg_nodes = []
|
arg_nodes = []
|
||||||
|
args.each do |arg|
|
||||||
args.each { |arg|
|
|
||||||
if (arg.is_a?(Asm::Register))
|
if (arg.is_a?(Asm::Register))
|
||||||
arg_nodes << arg
|
arg_nodes << arg
|
||||||
elsif (arg.is_a?(Integer))
|
elsif (arg.is_a?(Integer))
|
||||||
@ -39,38 +36,49 @@ module Asm
|
|||||||
elsif (arg.is_a?(Asm::Label))
|
elsif (arg.is_a?(Asm::Label))
|
||||||
arg_nodes << arg
|
arg_nodes << arg
|
||||||
else
|
else
|
||||||
raise 'Invalid argument `%s\' for instruction' % arg.inspect
|
raise "Invalid argument #{arg.inspect} for instruction"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
}
|
|
||||||
|
|
||||||
add_value clazz.new(opcode , arg_nodes)
|
add_value clazz.new(opcode , arg_nodes)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def self.define_instruction(inst , clazz )
|
def self.define_instruction(inst , clazz )
|
||||||
define_method(inst) do |*args|
|
define_method(inst) do |*args|
|
||||||
instruction clazz , inst.to_sym, *args
|
instruction clazz , inst , *args
|
||||||
end
|
end
|
||||||
define_method(inst+'s') do |*args|
|
define_method(inst.to_s+'s') do |*args|
|
||||||
instruction clazz , (inst+'s').to_sym, *args
|
instruction clazz , inst.to_s+'s' , *args
|
||||||
end
|
end
|
||||||
%w(al eq ne cs mi hi cc pl ls vc lt le ge gt vs).each do |cond_suffix|
|
InstructionTools::COND_CODES.keys.each do |cond_suffix|
|
||||||
define_method(inst+cond_suffix) do |*args|
|
suffix = cond_suffix.to_s
|
||||||
instruction clazz , (inst+cond_suffix).to_sym, *args
|
define_method(inst.to_s + suffix) do |*args|
|
||||||
|
instruction clazz , inst + suffix , *args
|
||||||
end
|
end
|
||||||
define_method(inst+'s'+cond_suffix) do |*args|
|
define_method(inst.to_s + 's'+ suffix) do |*args|
|
||||||
instruction clazz , (inst+'s'+cond_suffix).to_sym, *args
|
instruction clazz , inst.to_s + 's' + suffix, *args
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
["push", "pop"].each do |inst|
|
[:push, :pop].each do |inst|
|
||||||
define_instruction(inst , StackInstruction)
|
define_instruction(inst , StackInstruction)
|
||||||
end
|
end
|
||||||
|
|
||||||
%w(adc add and bic eor orr rsb rsc sbc sub mov mvn cmn cmp teq tst b bl bx
|
[:adc, :add, :and, :bic, :eor, :orr, :rsb, :rsc, :sbc, :sub].each do |inst|
|
||||||
swi str strb ldr ldrb ).each do |inst|
|
define_instruction(inst , LogicInstruction)
|
||||||
define_instruction(inst , Instruction)
|
end
|
||||||
|
[:mov, :mvn].each do |inst|
|
||||||
|
define_instruction(inst , MoveInstruction)
|
||||||
|
end
|
||||||
|
[:cmn, :cmp, :teq, :tst].each do |inst|
|
||||||
|
define_instruction(inst , CompareInstruction)
|
||||||
|
end
|
||||||
|
[:strb, :str , :ldrb, :ldr].each do |inst|
|
||||||
|
define_instruction(inst , MemoryInstruction)
|
||||||
|
end
|
||||||
|
[:b, :bl , :swi].each do |inst|
|
||||||
|
define_instruction(inst , CallInstruction)
|
||||||
end
|
end
|
||||||
|
|
||||||
def assemble_to_string
|
def assemble_to_string
|
||||||
|
43
lib/asm/call_instruction.rb
Normal file
43
lib/asm/call_instruction.rb
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
module Asm
|
||||||
|
# ADDRESSING MODE 4 , Calling
|
||||||
|
|
||||||
|
class CallInstruction < Instruction
|
||||||
|
include Asm::InstructionTools
|
||||||
|
|
||||||
|
def initialize(opcode , args)
|
||||||
|
super(opcode,args)
|
||||||
|
end
|
||||||
|
|
||||||
|
def assemble(io, as)
|
||||||
|
s = @update_status_flag? 1 : 0
|
||||||
|
case opcode
|
||||||
|
when :b, :bl
|
||||||
|
arg = args[0]
|
||||||
|
if arg.is_a? Label
|
||||||
|
diff = arg.position - self.position - 8
|
||||||
|
arg = NumLiteral.new(diff)
|
||||||
|
end
|
||||||
|
if (arg.is_a?(Asm::NumLiteral))
|
||||||
|
jmp_val = arg.value >> 2
|
||||||
|
packed = [jmp_val].pack('l')
|
||||||
|
# signed 32-bit, condense to 24-bit
|
||||||
|
# TODO add check that the value fits into 24 bits
|
||||||
|
io << packed[0,3]
|
||||||
|
else
|
||||||
|
raise "else not coded #{arg.inspect}"
|
||||||
|
end
|
||||||
|
io.write_uint8 OPCODES[opcode] | (COND_CODES[@cond] << 4)
|
||||||
|
when :swi
|
||||||
|
arg = args[0]
|
||||||
|
if (arg.is_a?(Asm::NumLiteral))
|
||||||
|
packed = [arg.value].pack('L')[0,3]
|
||||||
|
io << packed
|
||||||
|
io.write_uint8 0b1111 | (COND_CODES[@cond] << 4)
|
||||||
|
else
|
||||||
|
raise Asm::AssemblyError.new("invalid operand argument expected literal not #{arg}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end#class
|
||||||
|
end
|
@ -1,7 +1,5 @@
|
|||||||
require "asm/assembly_error"
|
require "asm/assembly_error"
|
||||||
require "asm/instruction_tools"
|
require "asm/instruction_tools"
|
||||||
require "asm/normal_builder"
|
|
||||||
require "asm/memory_access_builder"
|
|
||||||
require "asm/label"
|
require "asm/label"
|
||||||
|
|
||||||
module Asm
|
module Asm
|
||||||
@ -9,25 +7,26 @@ module Asm
|
|||||||
class Instruction
|
class Instruction
|
||||||
include InstructionTools
|
include InstructionTools
|
||||||
|
|
||||||
COND_POSTFIXES = Regexp.union(%w(eq ne cs cc mi pl vs vc hi ls ge lt gt le al)).source
|
COND_POSTFIXES = Regexp.union( COND_CODES.keys.collect{|k|k.to_s} ).source
|
||||||
def initialize(opcode , args)
|
|
||||||
|
|
||||||
opcode = opcode.downcase
|
def initialize(opcode , args)
|
||||||
@cond = 0b1011
|
opcode = opcode.to_s.downcase
|
||||||
|
@cond = :al
|
||||||
if (opcode =~ /(#{COND_POSTFIXES})$/)
|
if (opcode =~ /(#{COND_POSTFIXES})$/)
|
||||||
@cond = $1.to_sym
|
@cond = $1.to_sym
|
||||||
opcode = opcode[0..-3]
|
opcode = opcode[0..-3]
|
||||||
end unless opcode == 'teq'
|
end unless opcode == 'teq'
|
||||||
if (opcode =~ /s$/)
|
if (opcode =~ /s$/)
|
||||||
@s = true
|
@update_status_flag= 1
|
||||||
opcode = opcode[0..-2]
|
opcode = opcode[0..-2]
|
||||||
else
|
else
|
||||||
@s = false
|
@update_status_flag= 0
|
||||||
end
|
end
|
||||||
@opcode = opcode.downcase.to_sym
|
@opcode = opcode.downcase.to_sym
|
||||||
@args = args
|
@args = args
|
||||||
|
@operand = 0
|
||||||
end
|
end
|
||||||
attr_reader :opcode, :args , :position
|
attr_reader :opcode, :args , :position , :cond , :operand , :update_status_flag
|
||||||
|
|
||||||
def affect_status
|
def affect_status
|
||||||
@s
|
@s
|
||||||
@ -42,69 +41,7 @@ module Asm
|
|||||||
end
|
end
|
||||||
|
|
||||||
def assemble(io, as)
|
def assemble(io, as)
|
||||||
s = @s ? 1 : 0
|
raise "Abstract class, should not be called/instantiated #{self.inspect}"
|
||||||
case opcode
|
|
||||||
when :adc, :add, :and, :bic, :eor, :orr, :rsb, :rsc, :sbc, :sub
|
|
||||||
builder = NormalBuilder.new(OPC_DATA_PROCESSING, OPCODES[opcode], s)
|
|
||||||
builder.cond = COND_CODES[@cond]
|
|
||||||
builder.rd = reg_ref(args[0])
|
|
||||||
builder.rn = reg_ref(args[1])
|
|
||||||
builder.build_operand args[2] , self.position
|
|
||||||
builder.assemble io, as
|
|
||||||
when :cmn, :cmp, :teq, :tst
|
|
||||||
builder = NormalBuilder.new(OPC_DATA_PROCESSING, OPCODES[opcode], 1)
|
|
||||||
builder.cond = COND_CODES[@cond]
|
|
||||||
builder.rn = reg_ref(args[0])
|
|
||||||
builder.rd = 0
|
|
||||||
builder.build_operand args[1]
|
|
||||||
builder.assemble io, as
|
|
||||||
when :mov, :mvn
|
|
||||||
builder = NormalBuilder.new(OPC_DATA_PROCESSING, OPCODES[opcode], s)
|
|
||||||
builder.cond = COND_CODES[@cond]
|
|
||||||
builder.rn = 0
|
|
||||||
builder.rd = reg_ref(args[0])
|
|
||||||
builder.build_operand args[1]
|
|
||||||
builder.assemble io, as
|
|
||||||
when :strb, :str
|
|
||||||
builder = MemoryAccessBuilder.new(OPC_MEMORY_ACCESS, (opcode == :strb ? 1 : 0), 0)
|
|
||||||
builder.cond = COND_CODES[@cond]
|
|
||||||
builder.rd = reg_ref(args[1])
|
|
||||||
builder.build_operand args[0]
|
|
||||||
builder.assemble io, as, self
|
|
||||||
when :ldrb, :ldr
|
|
||||||
builder = MemoryAccessBuilder.new(OPC_MEMORY_ACCESS, (opcode == :ldrb ? 1 : 0), 1)
|
|
||||||
builder.cond = COND_CODES[@cond]
|
|
||||||
builder.rd = reg_ref(args[0])
|
|
||||||
builder.build_operand args[1]
|
|
||||||
builder.assemble io, as, self
|
|
||||||
when :b, :bl
|
|
||||||
arg = args[0]
|
|
||||||
if arg.is_a? Label
|
|
||||||
diff = arg.position - self.position - 8
|
|
||||||
arg = NumLiteral.new(diff)
|
|
||||||
end
|
|
||||||
if (arg.is_a?(Asm::NumLiteral))
|
|
||||||
jmp_val = arg.value >> 2
|
|
||||||
packed = [jmp_val].pack('l')
|
|
||||||
# signed 32-bit, condense to 24-bit
|
|
||||||
# TODO add check that the value fits into 24 bits
|
|
||||||
io << packed[0,3]
|
|
||||||
else
|
|
||||||
raise "else not coded #{arg.inspect}"
|
|
||||||
end
|
|
||||||
io.write_uint8 OPCODES[opcode] | (COND_CODES[@cond] << 4)
|
|
||||||
when :swi
|
|
||||||
arg = args[0]
|
|
||||||
if (arg.is_a?(Asm::NumLiteral))
|
|
||||||
packed = [arg.value].pack('L')[0,3]
|
|
||||||
io << packed
|
|
||||||
io.write_uint8 0b1111 | (COND_CODES[@cond] << 4)
|
|
||||||
else
|
|
||||||
raise Asm::AssemblyError.new("invalid operand argument expected literal not #{arg}")
|
|
||||||
end
|
|
||||||
else
|
|
||||||
raise Asm::AssemblyError.new("unknown instruction #{opcode} #{self}")
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
@ -21,6 +21,14 @@ module Asm
|
|||||||
:bl => 0b1011,
|
:bl => 0b1011,
|
||||||
:bx => 0b00010010
|
:bx => 0b00010010
|
||||||
}
|
}
|
||||||
|
#return the bit patter that the cpu uses for the current instruction @opcode
|
||||||
|
def op_bit_code
|
||||||
|
OPCODES[@opcode] or throw "no code found for #{@opcode.inspect}"
|
||||||
|
end
|
||||||
|
|
||||||
|
#codition codes can be applied to many instructions and thus save branches
|
||||||
|
# :al => always , :eq => equal and so on
|
||||||
|
# eq mov if equal :moveq r1 r2 (also exists as function) will only execute if the last operation was 0
|
||||||
COND_CODES = {
|
COND_CODES = {
|
||||||
:al => 0b1110, :eq => 0b0000,
|
:al => 0b1110, :eq => 0b0000,
|
||||||
:ne => 0b0001, :cs => 0b0010,
|
:ne => 0b0001, :cs => 0b0010,
|
||||||
@ -31,30 +39,27 @@ module Asm
|
|||||||
:ge => 0b1010, :gt => 0b1100,
|
:ge => 0b1010, :gt => 0b1100,
|
||||||
:vs => 0b0110
|
:vs => 0b0110
|
||||||
}
|
}
|
||||||
|
#return the bit pattern for the @cond variable, which signals the conditional code
|
||||||
|
def cond_bit_code
|
||||||
|
COND_CODES[@cond] or throw "no code found for #{@cond}"
|
||||||
|
end
|
||||||
|
|
||||||
OPC_DATA_PROCESSING = 0b00
|
OPC_DATA_PROCESSING = 0b00
|
||||||
OPC_MEMORY_ACCESS = 0b01
|
OPC_MEMORY_ACCESS = 0b01
|
||||||
OPC_STACK = 0b10
|
OPC_STACK = 0b10
|
||||||
|
|
||||||
def reg_ref(arg)
|
REGISTERS = { 'r0' => 0, 'r1' => 1, 'r2' => 2, 'r3' => 3, 'r4' => 4, 'r5' => 5,
|
||||||
if (not arg.is_a?(Asm::Register))
|
|
||||||
raise Asm::AssemblyError.new("argument must be a register not #{arg}")
|
|
||||||
end
|
|
||||||
|
|
||||||
ref =
|
|
||||||
{'r0' => 0, 'r1' => 1, 'r2' => 2, 'r3' => 3, 'r4' => 4, 'r5' => 5,
|
|
||||||
'r6' => 6, 'r7' => 7, 'r8' => 8, 'r9' => 9, 'r10' => 10, 'r11' => 11,
|
'r6' => 6, 'r7' => 7, 'r8' => 8, 'r9' => 9, 'r10' => 10, 'r11' => 11,
|
||||||
'r12' => 12, 'r13' => 13, 'r14' => 14, 'r15' => 15, 'a1' => 0, 'a2' => 1,
|
'r12' => 12, 'r13' => 13, 'r14' => 14, 'r15' => 15, 'a1' => 0, 'a2' => 1,
|
||||||
'a3' => 2, 'a4' => 3, 'v1' => 4, 'v2' => 5, 'v3' => 6, 'v4' => 7, 'v5' => 8,
|
'a3' => 2, 'a4' => 3, 'v1' => 4, 'v2' => 5, 'v3' => 6, 'v4' => 7, 'v5' => 8,
|
||||||
'v6' => 9, 'rfp' => 9, 'sl' => 10, 'fp' => 11, 'ip' => 12, 'sp' => 13,
|
'v6' => 9, 'rfp' => 9, 'sl' => 10, 'fp' => 11, 'ip' => 12, 'sp' => 13,
|
||||||
'lr' => 14, 'pc' => 15}[arg.name.downcase]
|
'lr' => 14, 'pc' => 15 }
|
||||||
|
def reg name
|
||||||
if (not ref)
|
raise "no such register #{reg}" unless REGISTERS[name]
|
||||||
raise Asm::AssemblyError.new("unknown register #{arg}")
|
Asm::Register.new(name , REGISTERS[name])
|
||||||
end
|
end
|
||||||
|
|
||||||
ref
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def calculate_u8_with_rr(arg)
|
def calculate_u8_with_rr(arg)
|
||||||
parts = arg.value.to_s(2).rjust(32,'0').scan(/^(0*)(.+?)0*$/).flatten
|
parts = arg.value.to_s(2).rjust(32,'0').scan(/^(0*)(.+?)0*$/).flatten
|
||||||
@ -77,5 +82,5 @@ module Asm
|
|||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
end
|
end
|
105
lib/asm/logic_instruction.rb
Normal file
105
lib/asm/logic_instruction.rb
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
module Asm
|
||||||
|
# ADDRESSING MODE 1
|
||||||
|
# Logic ,Maths, Move and compare instructions (last three below)
|
||||||
|
|
||||||
|
class LogicInstruction < Instruction
|
||||||
|
include Asm::InstructionTools
|
||||||
|
|
||||||
|
def initialize( opcode , args)
|
||||||
|
super(opcode , args)
|
||||||
|
@inst_class = OPC_DATA_PROCESSING
|
||||||
|
@i = 0
|
||||||
|
@rd = args[0]
|
||||||
|
end
|
||||||
|
attr_accessor :inst_class, :i, :rn, :rd
|
||||||
|
|
||||||
|
# Build representation for source value
|
||||||
|
def build
|
||||||
|
@rn = args[1]
|
||||||
|
do_build args[2]
|
||||||
|
end
|
||||||
|
|
||||||
|
#(stays in subclases, while build is overriden to provide different arguments)
|
||||||
|
def do_build(arg)
|
||||||
|
if arg.is_a?(Asm::StringLiteral)
|
||||||
|
# do pc relative addressing with the difference to the instuction
|
||||||
|
# 8 is for the funny pipeline adjustment (ie oc pointing to fetch and not execute)
|
||||||
|
arg = Asm::NumLiteral.new( arg.position - self.position - 8 )
|
||||||
|
end
|
||||||
|
if (arg.is_a?(Asm::NumLiteral))
|
||||||
|
if (arg.value.fits_u8?)
|
||||||
|
# no shifting needed
|
||||||
|
@operand = arg.value
|
||||||
|
@i = 1
|
||||||
|
elsif (op_with_rot = calculate_u8_with_rr(arg))
|
||||||
|
@operand = op_with_rot
|
||||||
|
@i = 1
|
||||||
|
else
|
||||||
|
raise Asm::AssemblyError.new("cannot fit numeric literal argument in operand #{arg}")
|
||||||
|
end
|
||||||
|
elsif (arg.is_a?(Asm::Register))
|
||||||
|
@operand = arg
|
||||||
|
@i = 0
|
||||||
|
elsif (arg.is_a?(Asm::Shift))
|
||||||
|
rm_ref = arg.argument
|
||||||
|
@i = 0
|
||||||
|
shift_op = {'lsl' => 0b000, 'lsr' => 0b010, 'asr' => 0b100,
|
||||||
|
'ror' => 0b110, 'rrx' => 0b110}[arg.type]
|
||||||
|
if (arg.type == 'ror' and arg.value.nil?)
|
||||||
|
# ror #0 == rrx
|
||||||
|
raise Asm::AssemblyError.new('cannot rotate by zero', arg)
|
||||||
|
end
|
||||||
|
|
||||||
|
arg1 = arg.value
|
||||||
|
if (arg1.is_a?(Asm::NumLiteral))
|
||||||
|
if (arg1.value >= 32)
|
||||||
|
raise Asm::AssemblyError.new('cannot shift by more than 31', arg1)
|
||||||
|
end
|
||||||
|
shift_imm = arg1.value
|
||||||
|
elsif (arg1.is_a?(Asm::Register))
|
||||||
|
shift_op val |= 0x1;
|
||||||
|
shift_imm = arg1.number << 1
|
||||||
|
elsif (arg.type == 'rrx')
|
||||||
|
shift_imm = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
@operand = rm_ref | (shift_op << 4) | (shift_imm << 4+3)
|
||||||
|
else
|
||||||
|
raise Asm::AssemblyError.new("invalid operand argument #{arg.inspect}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def assemble(io, as)
|
||||||
|
build
|
||||||
|
val = operand.is_a?(Register) ? operand.bits : operand
|
||||||
|
val |= (rd.bits << 12)
|
||||||
|
val |= (rn.bits << 12+4)
|
||||||
|
val |= (update_status_flag << 12+4+4)#20
|
||||||
|
val |= (op_bit_code << 12+4+4 +1)
|
||||||
|
val |= (i << 12+4+4 +1+4)
|
||||||
|
val |= (inst_class << 12+4+4 +1+4+1)
|
||||||
|
val |= (cond_bit_code << 12+4+4 +1+4+1+2)
|
||||||
|
io.write_uint32 val
|
||||||
|
end
|
||||||
|
end
|
||||||
|
class CompareInstruction < LogicInstruction
|
||||||
|
def initialize( opcode , args)
|
||||||
|
super(opcode , args)
|
||||||
|
@update_status_flag = 1
|
||||||
|
@rn = args[0]
|
||||||
|
@rd = reg "r0"
|
||||||
|
end
|
||||||
|
def build
|
||||||
|
do_build args[1]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
class MoveInstruction < LogicInstruction
|
||||||
|
def initialize( opcode , args)
|
||||||
|
super(opcode , args)
|
||||||
|
@rn = reg "r0" # register zero = zero bit pattern
|
||||||
|
end
|
||||||
|
def build
|
||||||
|
do_build args[1]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -1,74 +0,0 @@
|
|||||||
require "asm/nodes"
|
|
||||||
|
|
||||||
module Asm
|
|
||||||
# ADDRESSING MODE 2
|
|
||||||
# Implemented: immediate offset with offset=0
|
|
||||||
class MemoryAccessBuilder
|
|
||||||
include Asm::InstructionTools
|
|
||||||
|
|
||||||
def initialize(inst_class, byte_access, load_store)
|
|
||||||
@cond = 0b1110
|
|
||||||
@inst_class = 0
|
|
||||||
@i = 0 #I flag (third bit)
|
|
||||||
@pre_post_index = 0 #P flag
|
|
||||||
@add_offset = 0 #U flag
|
|
||||||
@byte_access = 0 #B flag
|
|
||||||
@w = 0 #W flag
|
|
||||||
@load_store = 0 #L flag
|
|
||||||
@rn = 0
|
|
||||||
@rd = 0
|
|
||||||
@operand = 0
|
|
||||||
@inst_class = inst_class
|
|
||||||
@byte_access = byte_access
|
|
||||||
@load_store = load_store
|
|
||||||
end
|
|
||||||
attr_accessor :cond, :inst_class, :i, :pre_post_index, :add_offset,
|
|
||||||
:byte_access, :w, :load_store, :rn, :rd, :operand
|
|
||||||
|
|
||||||
# Build representation for target address
|
|
||||||
def build_operand(arg)
|
|
||||||
#str / ldr are _serious instructions. With BIG possibilities not half are implemented
|
|
||||||
@i = 0
|
|
||||||
@pre_post_index = 0
|
|
||||||
@w = 0
|
|
||||||
@operand = 0
|
|
||||||
if (arg.is_a?(Asm::Register))
|
|
||||||
@rn = reg_ref(arg)
|
|
||||||
if(arg.offset != 0)
|
|
||||||
@operand = arg.offset
|
|
||||||
if (@operand < 0)
|
|
||||||
@add_offset = 0
|
|
||||||
#TODO test/check/understand
|
|
||||||
@operand *= -1
|
|
||||||
else
|
|
||||||
@add_offset = 1
|
|
||||||
end
|
|
||||||
if (@operand.abs > 4095)
|
|
||||||
raise Asm::AssemblyError.new("reference offset too large/small (max 4095) #{argr.right}" )
|
|
||||||
end
|
|
||||||
end
|
|
||||||
elsif (arg.is_a?(Asm::Label) or arg.is_a?(Asm::NumLiteral))
|
|
||||||
@pre_post_index = 1
|
|
||||||
@rn = 15 # pc
|
|
||||||
@use_addrtable_reloc = true
|
|
||||||
@addrtable_reloc_target = arg
|
|
||||||
else
|
|
||||||
raise Asm::AssemblyError.new("invalid operand argument #{arg.inspect}")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def assemble(io, as, inst)
|
|
||||||
#not sure about these 2 constants. They produce the correct output for str r0 , r1
|
|
||||||
# but i can't help thinking that that is because they are not used in that instruction and
|
|
||||||
# so it doesn't matter. Will see
|
|
||||||
@add_offset = 1
|
|
||||||
@pre_post_index = 1
|
|
||||||
val = operand | (rd << 12 ) | (rn << 12 + 4) |
|
|
||||||
(load_store << 12+4+4) | (w << 12+4+4+1) |
|
|
||||||
(byte_access << 12+4+4+1+1) | (add_offset << 12+4+4+1+1+1) |
|
|
||||||
(pre_post_index << 12+4+4+1+1+1+1) | (i << 12+4+4+1+1+1+1+1) |
|
|
||||||
(inst_class << 12+4+4+1+1+1+1+1+1) | (cond << 12+4+4+1+1+1+1+1+1+2)
|
|
||||||
io.write_uint32 val
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
80
lib/asm/memory_instruction.rb
Normal file
80
lib/asm/memory_instruction.rb
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
require "asm/nodes"
|
||||||
|
|
||||||
|
module Asm
|
||||||
|
# ADDRESSING MODE 2
|
||||||
|
# Implemented: immediate offset with offset=0
|
||||||
|
class MemoryInstruction < Instruction
|
||||||
|
include Asm::InstructionTools
|
||||||
|
|
||||||
|
def initialize(opcode , args)
|
||||||
|
super( opcode , args )
|
||||||
|
@inst_class = OPC_MEMORY_ACCESS
|
||||||
|
@i = 0 #I flag (third bit)
|
||||||
|
@pre_post_index = 0 #P flag
|
||||||
|
@add_offset = 0 #U flag
|
||||||
|
@byte_access = opcode.to_s[-1] == "b" ? 1 : 0 #B (byte) flag
|
||||||
|
@w = 0 #W flag
|
||||||
|
@is_load = opcode.to_s[0] == "l" ? 1 : 0 #L (load) flag
|
||||||
|
@rn = reg "r0" # register zero = zero bit pattern
|
||||||
|
@rd = reg "r0" # register zero = zero bit pattern
|
||||||
|
end
|
||||||
|
attr_accessor :inst_class, :i, :pre_post_index, :add_offset,
|
||||||
|
:byte_access, :w, :is_load, :rn, :rd
|
||||||
|
|
||||||
|
# Build representation for target address
|
||||||
|
def build
|
||||||
|
if( @is_load )
|
||||||
|
@rd = args[0]
|
||||||
|
arg = args[1]
|
||||||
|
else #store
|
||||||
|
@rd = args[1]
|
||||||
|
arg = args[0]
|
||||||
|
end
|
||||||
|
#str / ldr are _serious instructions. With BIG possibilities not half are implemented
|
||||||
|
if (arg.is_a?(Asm::Register))
|
||||||
|
@rn = arg
|
||||||
|
if(arg.offset != 0)
|
||||||
|
@operand = arg.offset
|
||||||
|
if (@operand < 0)
|
||||||
|
@add_offset = 0
|
||||||
|
#TODO test/check/understand
|
||||||
|
@operand *= -1
|
||||||
|
else
|
||||||
|
@add_offset = 1
|
||||||
|
end
|
||||||
|
if (@operand.abs > 4095)
|
||||||
|
raise Asm::AssemblyError.new("reference offset too large/small (max 4095) #{argr.right}" )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elsif (arg.is_a?(Asm::Label) or arg.is_a?(Asm::NumLiteral))
|
||||||
|
@pre_post_index = 1
|
||||||
|
@rn = pc
|
||||||
|
@use_addrtable_reloc = true
|
||||||
|
@addrtable_reloc_target = arg
|
||||||
|
else
|
||||||
|
raise Asm::AssemblyError.new("invalid operand argument #{arg.inspect}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def assemble(io, as)
|
||||||
|
build
|
||||||
|
#not sure about these 2 constants. They produce the correct output for str r0 , r1
|
||||||
|
# but i can't help thinking that that is because they are not used in that instruction and
|
||||||
|
# so it doesn't matter. Will see
|
||||||
|
@add_offset = 1
|
||||||
|
@pre_post_index = 1
|
||||||
|
val = operand
|
||||||
|
val |= (rd.bits << 12 )
|
||||||
|
val |= (rn.bits << 12+4) #16
|
||||||
|
val |= (is_load << 12+4 +4)
|
||||||
|
val |= (w << 12+4 +4+1)
|
||||||
|
val |= (byte_access << 12+4 +4+1+1)
|
||||||
|
val |= (add_offset << 12+4 +4+1+1+1)
|
||||||
|
val |= (pre_post_index << 12+4 +4+1+1+1+1)#24
|
||||||
|
val |= (i << 12+4 +4+1+1+1+1 +1)
|
||||||
|
val |= (inst_class << 12+4 +4+1+1+1+1 +1+1)
|
||||||
|
val |= (cond_bit_code << 12+4 +4+1+1+1+1 +1+1+2)
|
||||||
|
io.write_uint32 val
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -9,9 +9,10 @@ module Asm
|
|||||||
# Arm has addressing modes abound, and so can add to a register before actually using it
|
# Arm has addressing modes abound, and so can add to a register before actually using it
|
||||||
# If can actually shift or indeed shift what it adds, but not implemented
|
# If can actually shift or indeed shift what it adds, but not implemented
|
||||||
class Register
|
class Register
|
||||||
attr_accessor :name , :offset
|
attr_accessor :name , :offset , :bits
|
||||||
def initialize name
|
def initialize name , bits
|
||||||
@name = name
|
@name = name
|
||||||
|
@bits = bits
|
||||||
@offset = 0
|
@offset = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1,82 +0,0 @@
|
|||||||
module Asm
|
|
||||||
# ADDRESSING MODE 1
|
|
||||||
# Complete!
|
|
||||||
class NormalBuilder
|
|
||||||
include Asm::InstructionTools
|
|
||||||
|
|
||||||
def initialize(inst_class, opcode, s)
|
|
||||||
@cond = 0b1110
|
|
||||||
@inst_class = 0
|
|
||||||
@i = 0
|
|
||||||
@s = 0
|
|
||||||
@rn = 0
|
|
||||||
@rd = 0
|
|
||||||
@operand = 0
|
|
||||||
@inst_class = inst_class
|
|
||||||
@opcode = opcode
|
|
||||||
@s = s
|
|
||||||
end
|
|
||||||
attr_accessor :cond, :inst_class, :i, :opcode, :s,
|
|
||||||
:rn, :rd, :operand
|
|
||||||
|
|
||||||
# Build representation for source value
|
|
||||||
def build_operand(arg , position = 0)
|
|
||||||
#position only needed for calculating relative addresses to data objects
|
|
||||||
#there is a design stink here which makes my head ache. But shanti shanti
|
|
||||||
if arg.is_a?(Asm::StringLiteral)
|
|
||||||
# do pc relative addressing with the difference to the instuction
|
|
||||||
# 8 is for the funny pipeline adjustment (ie oc pointing to fetch and not execute)
|
|
||||||
arg = Asm::NumLiteral.new( arg.position - position - 8 )
|
|
||||||
end
|
|
||||||
if (arg.is_a?(Asm::NumLiteral))
|
|
||||||
if (arg.value.fits_u8?)
|
|
||||||
# no shifting needed
|
|
||||||
@operand = arg.value
|
|
||||||
@i = 1
|
|
||||||
elsif (op_with_rot = calculate_u8_with_rr(arg))
|
|
||||||
@operand = op_with_rot
|
|
||||||
@i = 1
|
|
||||||
else
|
|
||||||
raise Asm::AssemblyError.new("cannot fit numeric literal argument in operand #{arg}")
|
|
||||||
end
|
|
||||||
elsif (arg.is_a?(Asm::Register))
|
|
||||||
@operand = reg_ref(arg)
|
|
||||||
@i = 0
|
|
||||||
elsif (arg.is_a?(Asm::Shift))
|
|
||||||
rm_ref = reg_ref(arg.argument)
|
|
||||||
@i = 0
|
|
||||||
shift_op = {'lsl' => 0b000, 'lsr' => 0b010, 'asr' => 0b100,
|
|
||||||
'ror' => 0b110, 'rrx' => 0b110}[arg.type]
|
|
||||||
if (arg.type == 'ror' and arg.value.nil?)
|
|
||||||
# ror #0 == rrx
|
|
||||||
raise Asm::AssemblyError.new('cannot rotate by zero', arg)
|
|
||||||
end
|
|
||||||
|
|
||||||
arg1 = arg.value
|
|
||||||
if (arg1.is_a?(Asm::NumLiteral))
|
|
||||||
if (arg1.value >= 32)
|
|
||||||
raise Asm::AssemblyError.new('cannot shift by more than 31', arg1)
|
|
||||||
end
|
|
||||||
shift_imm = arg1.value
|
|
||||||
elsif (arg1.is_a?(Asm::Register))
|
|
||||||
shift_op |= 0x1;
|
|
||||||
shift_imm = reg_ref(arg1) << 1
|
|
||||||
elsif (arg.type == 'rrx')
|
|
||||||
shift_imm = 0
|
|
||||||
end
|
|
||||||
|
|
||||||
@operand = rm_ref | (shift_op << 4) | (shift_imm << 4+3)
|
|
||||||
else
|
|
||||||
raise Asm::AssemblyError.new("invalid operand argument #{arg.inspect}")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def assemble(io, as)
|
|
||||||
val = operand | (rd << 12) | (rn << 12+4) |
|
|
||||||
(s << 12+4+4) | (opcode << 12+4+4+1) |
|
|
||||||
(i << 12+4+4+1+4) | (inst_class << 12+4+4+1+4+1) |
|
|
||||||
(cond << 12+4+4+1+4+1+2)
|
|
||||||
io.write_uint32 val
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -7,59 +7,53 @@ module Asm
|
|||||||
|
|
||||||
def initialize(opcode , args)
|
def initialize(opcode , args)
|
||||||
super(opcode,args)
|
super(opcode,args)
|
||||||
@operand = 0
|
|
||||||
@cond = 0b1110
|
|
||||||
@inst_class = Asm::Instruction::OPC_STACK
|
@inst_class = Asm::Instruction::OPC_STACK
|
||||||
@s = 0
|
@update_status_flag= 0
|
||||||
@rn = 0
|
@rn = reg "r0" # register zero = zero bit pattern
|
||||||
# downward growing, decrement before memory access
|
# downward growing, decrement before memory access
|
||||||
# official ARM style stack as used by gas
|
# official ARM style stack as used by gas
|
||||||
@write_base = 1
|
@write_base = 1
|
||||||
if (opcode == :push)
|
if (opcode == :push)
|
||||||
@pre_post_index = 1
|
@pre_post_index = 1
|
||||||
@up_down = 0
|
@up_down = 0
|
||||||
@store_load = 0
|
@is_pop = 0
|
||||||
else #pop
|
else #pop
|
||||||
@pre_post_index = 0
|
@pre_post_index = 0
|
||||||
@up_down = 1
|
@up_down = 1
|
||||||
@store_load = 1
|
@is_pop = 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
attr_accessor :cond, :inst_class, :pre_post_index, :up_down,
|
attr_accessor :cond, :inst_class, :pre_post_index, :up_down,
|
||||||
:s, :write_base, :store_load, :rn, :operand
|
:update_status_flag, :write_base, :is_pop, :rn, :operand
|
||||||
|
|
||||||
def assemble(io, as)
|
def assemble(io, as)
|
||||||
|
build
|
||||||
cond = @cond.is_a?(Symbol) ? COND_CODES[@cond] : @cond
|
cond = @cond.is_a?(Symbol) ? COND_CODES[@cond] : @cond
|
||||||
rn = 13 # sp
|
rn = reg "sp" # sp register
|
||||||
build_operand args
|
|
||||||
|
|
||||||
#assemble of old
|
#assemble of old
|
||||||
val = @operand
|
val = operand
|
||||||
val |= (rn << 16)
|
val |= (rn.bits << 16)
|
||||||
val |= (store_load << 16+4) #20
|
val |= (is_pop << 16+4) #20
|
||||||
val |= (write_base << 16+4+ 1)
|
val |= (write_base << 16+4+ 1)
|
||||||
val |= (s << 16+4+ 1+1)
|
val |= (update_status_flag << 16+4+ 1+1)
|
||||||
val |= (up_down << 16+4+ 1+1+1)
|
val |= (up_down << 16+4+ 1+1+1)
|
||||||
val |= (pre_post_index << 16+4+ 1+1+1+1)#24
|
val |= (pre_post_index << 16+4+ 1+1+1+1)#24
|
||||||
val |= (inst_class << 16+4+ 1+1+1+1 +2)
|
val |= (inst_class << 16+4+ 1+1+1+1 +2)
|
||||||
val |= (cond << 16+4+ 1+1+1+1 +2+2)
|
val |= (cond << 16+4+ 1+1+1+1 +2+2)
|
||||||
puts "#{self.inspect}"
|
|
||||||
io.write_uint32 val
|
io.write_uint32 val
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
# Build representation for source value
|
# Build representation for source value
|
||||||
def build_operand(arg)
|
def build
|
||||||
if (arg.is_a?(Array))
|
if (args.is_a?(Array))
|
||||||
@operand = 0
|
@operand = 0
|
||||||
arg.each do |reg |
|
args.each do |reg |
|
||||||
reg = reg_ref(reg)
|
@operand |= (1 << reg.bits)
|
||||||
@operand |= (1 << reg)
|
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
raise Asm::AssemblyError.new("invalid operand argument #{arg.inspect}")
|
raise Asm::AssemblyError.new("invalid operand argument #{args.inspect}")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
Loading…
Reference in New Issue
Block a user