113 lines
3.2 KiB
Ruby
113 lines
3.2 KiB
Ruby
require_relative 'call_instruction'
|
|
require_relative 'stack_instruction'
|
|
require_relative 'logic_instruction'
|
|
require_relative 'memory_instruction'
|
|
|
|
module Asm
|
|
|
|
class Code ; end
|
|
|
|
# A Block is the smalles unit of code, a list of instructions as it were
|
|
# It is also a point to jump/branch to. An address in the final stream.
|
|
# To allow for forward branches creation does not fix the position. Either set or assembling does that.
|
|
|
|
# Blocks are also used to create instructions, and so Block has functions for every cpu instruction
|
|
# and to make using the apu function easier, there are functions that create registers as well
|
|
class Block < Code
|
|
|
|
def initialize(asm)
|
|
super()
|
|
@codes = []
|
|
@position = 0
|
|
@asm = asm
|
|
end
|
|
|
|
ArmMachine::REGISTERS.each do |reg , number|
|
|
define_method(reg) { Asm::Register.new(reg , number) }
|
|
end
|
|
|
|
def instruction(clazz, opcode , condition_code , update_status , *args)
|
|
arg_nodes = []
|
|
args.each do |arg|
|
|
if (arg.is_a?(Asm::Register))
|
|
arg_nodes << arg
|
|
elsif (arg.is_a?(Integer))
|
|
arg_nodes << Asm::NumLiteral.new(arg)
|
|
elsif (arg.is_a?(String))
|
|
arg_nodes << @asm.add_string(arg)
|
|
elsif (arg.is_a?(Asm::Label))
|
|
arg_nodes << arg
|
|
else
|
|
raise "Invalid argument #{arg.inspect} for instruction"
|
|
end
|
|
end
|
|
add_code clazz.new(opcode , condition_code , update_status , arg_nodes)
|
|
end
|
|
|
|
|
|
def self.define_instruction(inst , clazz )
|
|
define_method(inst) do |*args|
|
|
instruction clazz , inst , :al , 0 , *args
|
|
end
|
|
define_method("#{inst}s") do |*args|
|
|
instruction clazz , inst , :al , 1 , *args
|
|
end
|
|
ArmMachine::COND_CODES.keys.each do |suffix|
|
|
define_method("#{inst}#{suffix}") do |*args|
|
|
instruction clazz , inst , suffix , 0 , *args
|
|
end
|
|
define_method("#{inst}s#{suffix}") do |*args|
|
|
instruction clazz , inst , suffix , 1 , *args
|
|
end
|
|
end
|
|
end
|
|
|
|
[:push, :pop].each do |inst|
|
|
define_instruction(inst , StackInstruction)
|
|
end
|
|
|
|
[:adc, :add, :and, :bic, :eor, :orr, :rsb, :rsc, :sbc, :sub].each do |inst|
|
|
define_instruction(inst , LogicInstruction)
|
|
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
|
|
|
|
# setting a block fixes it's position in the stream.
|
|
# For backwards jumps, positions of blocks are known at creation, but for forward off course not.
|
|
# So then one can create a block, branch to it and set it later.
|
|
def set!
|
|
@asm.add_block self
|
|
self
|
|
end
|
|
|
|
# Label has no length , 0
|
|
def length
|
|
@codes.sum :length
|
|
end
|
|
|
|
def add_code(kode)
|
|
kode.at(@position)
|
|
length = kode.length
|
|
@position += length
|
|
@codes << kode
|
|
end
|
|
|
|
def assemble(io)
|
|
@codes.each do |obj|
|
|
obj.assemble io
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
end |