better logic with new block class

This commit is contained in:
Torsten Ruger
2014-04-25 18:37:19 +03:00
parent 7af46d210b
commit 92beb638de
9 changed files with 189 additions and 137 deletions

View File

@ -1,32 +1,111 @@
require_relative "code"
require_relative 'call_instruction'
require_relative 'stack_instruction'
require_relative 'logic_instruction'
require_relative 'memory_instruction'
module Asm
# Labels are, like in assembler, a point to jump/branch to. An address in the stream.
# To allow for forward branches creation does not fix the position. Set does that.
class Label < Code
def initialize(name , asm)
super
@name = name
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
# setting a label fixes it's position in the stream.
# For backwards jumps, positions of labels are known at creation, but for forward off course not.
# So then one can create a label, branch to it and set it later.
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_value self
@asm.add_block self
self
end
# Label has no length , 0
def length
0
@codes.sum :length
end
def add_code(kode)
kode.at(@position)
length = kode.length
@position += length
@codes << kode
end
# nothing to write, we check that the position is what was set
def assemble(io)
raise "Hmm hmm hmm, me thinks i should be somewhere else" if self.position != io.tell
@codes.each do |obj|
obj.assemble io
end
end
end

View File

@ -1,3 +1,5 @@
require_relative "instruction"
module Asm
# There are only three call instructions in arm branch (b), call (bl) and syscall (swi)
@ -17,7 +19,7 @@ module Asm
case opcode
when :b, :bl
arg = args[0]
if arg.is_a? Label
if arg.is_a? Block
diff = arg.position - self.position - 8
arg = NumLiteral.new(diff)
end

View File

@ -1,9 +1,10 @@
require_relative "label"
require_relative "assembly_error"
require_relative "arm_machine"
module Asm
class Code ; end
# Not surprisingly represents an cpu instruction.
# This is an abstract base class, with derived classes
# Logic / Move / Compare / Stack / Memory (see there)

View File

@ -1,3 +1,5 @@
require_relative "instruction"
module Asm
# ADDRESSING MODE 1
# Logic ,Maths, Move and compare instructions (last three below)

View File

@ -1,4 +1,5 @@
require "asm/nodes"
require_relative "instruction"
module Asm
# ADDRESSING MODE 2

View File

@ -1,125 +1,90 @@
require 'asm/call_instruction'
require 'asm/stack_instruction'
require 'asm/logic_instruction'
require 'asm/memory_instruction'
require 'asm/nodes'
require 'asm/block'
require 'stream_reader'
require 'stringio'
require "asm/string_literal"
module Asm
class ArmAssembler
ArmMachine::REGISTERS.each do |reg , number|
define_method(reg) { Asm::Register.new(reg , number) }
end
# Program is the the top-level of the code hierachy, except it is not derived from code
# instead a Program is a list of blocks (and string constants)
# All code is created in blocks (see there) and there are two styles for that, for forward of backward
# referencing. Read function block and add_block and Block.set
class Program
def initialize
@codes = []
@position = 0 # marks not set
@labels = []
@blocks = []
@string_table = {}
end
attr_reader :codes , :position
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 << 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
attr_reader :blocks
[: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
# Assembling to string will return a binary string of the whole program, ie all blocks and the
# strings they use
# As a memory reference this would be callable, but more likely you will hand it over to
# an ObjectWriter as the .text section and then link it. And then execute it :-)
def assemble_to_string
#put the strings at the end of the assembled code.
# adding them will fix their position and make them assemble after
@string_table.values.each do |data|
add_code data
add_block data
end
io = StringIO.new
assemble(io)
io.string
end
# Add a string to the string table. Strings are global and constant. So only one copy of each
# string exists
# Internally StringLiterals are created and stored and during assembly written after the blocks
def add_string str
code = @string_table[str]
return code if code
data = Asm::StringLiteral.new(str)
@string_table[str] = data
end
def strings
@string_table.values
end
def add_code(kode)
kode.at(@position)
length = kode.length
@position += length
@codes << kode
end
def label name
label = Label.new(name , self)
@labels << label
label
# Length of all blocks. Does not take strings into account as they are added after all blocks.
# This is used to determine where a block when it is added after creation (see add_block)
def length
@blocks.inject(0) {| sum , item | sum + item.length}
end
# call block to create a new (code) block. The simple way is to do this with a block and
# use the yielded block to add code, ie something like:
# prog.block do |loop|
# loop.instance_eval do #this part you can acheive with calls too
# mov r0 , 10
# subs r0 , 1
# bne block
# end
# end
# Easy, because it's a backward jump. For forward branches that doesn't work and so you have to
# create the block without a ruby block. You can then jumpt to it immediately
# But the block is not part of the program (since we don't know where) and so you have to add it later
def block
block = Block.new(self)
yield block.set! if block_given? #yield the block (which set returns)
block
end
# This is how you add a forward declared block. This is called automatically when you
# call block with ruby block, but has to be done manually if not
def add_block block
block.at self.length
@blocks << block
end
private
def assemble(io)
@codes.each do |obj|
@blocks.each do |obj|
obj.assemble io
end
end
end
end

View File

@ -1,4 +1,4 @@
require "asm/instruction"
require_relative "instruction"
module Asm
# ADDRESSING MODE 4

View File

@ -2,8 +2,7 @@
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', ".." , "parslet",'lib'))
require 'parslet'
require "asm/stack_instruction"
require "asm/arm_assembler"
require "asm/program"
require "elf/object_writer"
require 'vm/parser'
require 'vm/nodes'