diff --git a/lib/arm/arm_machine.rb b/lib/arm/arm_machine.rb index 7daa20df..ce5f4172 100644 --- a/lib/arm/arm_machine.rb +++ b/lib/arm/arm_machine.rb @@ -33,8 +33,16 @@ module Arm def word_load value , reg mov( :left => reg , :right => value ) end + def string_load str_lit , reg + [ add( :left => "r#{reg}".to_sym , :extra => str_lit ) , #right is pc, implicit + #second arg is a hack to get the stringlength without coding + mov( :left => "r#{reg+1}".to_sym , :right => str_lit.length ) ] + end + def function_call call raise "Not FunctionCall #{call.inspect}" unless call.is_a? Vm::FunctionCall + call.args.each do | arg | + end bl( :left => call.function ) end @@ -44,18 +52,28 @@ module Arm end def main_exit entry = Vm::Block.new("main_exit") - entry.add_code syscall(0) + entry.add_code syscall(1) end def function_entry f_name entry = Vm::Block.new("#{f_name}_entry") - entry.add_code push( :left => :lr ) +# entry.add_code push( :left => :lr ) end def function_exit f_name entry = Vm::Block.new("#{f_name}_exit") - entry.add_code pop( :left => :pc ) + entry.add_code mov( :left => :pc , :right => :lr ) end + def putstring + put = Vm::Block.new("putstring_code") + # should be another level of indirection, ie write(io,str) + put.add_code mov( :left => :r2 , :right => :r1 ) + put.add_code mov( :left => :r1 , :right => :r0 ) + put.add_code mov( :left => :r0 , :right => 1 ) #stdout + put.add_code syscall(4) + end + private def syscall num [mov( :left => :r7 , :right => num ) , swi( :left => 0 )] end + end end \ No newline at end of file diff --git a/lib/arm/logic_instruction.rb b/lib/arm/logic_instruction.rb index a815889f..fe75da44 100644 --- a/lib/arm/logic_instruction.rb +++ b/lib/arm/logic_instruction.rb @@ -4,11 +4,6 @@ module Arm module LogicHelper # ADDRESSING MODE 1 # Logic ,Maths, Move and compare instructions (last three below) - # Build representation for source value - def build - @rn = @args[1] - do_build @args[2] - end # arm intrucioons are pretty sensible, and always 4 bytes (thumb not supported) def length @@ -21,6 +16,7 @@ module Arm # 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 = Arm::NumLiteral.new( arg.position - self.position - 8 ) + @rn = :pc end if( arg.is_a? Fixnum ) #HACK to not have to change the code just now arg = Arm::NumLiteral.new( arg ) @@ -36,7 +32,7 @@ module Arm else raise "cannot fit numeric literal argument in operand #{arg}" end - elsif (arg.is_a?(Arm::Register)) + elsif (arg.is_a?(Symbol)) @operand = arg @i = 0 elsif (arg.is_a?(Arm::Shift)) @@ -64,7 +60,7 @@ module Arm @operand = rm_ref | (shift_op << 4) | (shift_imm << 4+3) else - raise "invalid operand argument #{arg.inspect}" + raise "invalid operand argument #{arg.inspect} , #{inspect}" end end @@ -87,7 +83,7 @@ module Arm include LogicHelper def initialize(options) - super(options) + super(options) @update_status_flag = 0 @condition_code = :al @opcode = options[:opcode] @@ -96,9 +92,14 @@ module Arm @rn = nil @i = 0 - @rd = args[0] + @rd = @args[0] end attr_accessor :i, :rn, :rd + # Build representation for source value + def build + @rn = @args[1] + do_build @args[2] + end end class CompareInstruction < Vm::CompareInstruction diff --git a/lib/arm/memory_instruction.rb b/lib/arm/memory_instruction.rb index 88ef6b29..f12ea24d 100644 --- a/lib/arm/memory_instruction.rb +++ b/lib/arm/memory_instruction.rb @@ -7,16 +7,22 @@ module Arm class MemoryInstruction < Vm::MemoryInstruction include Arm::Constants - def initialize(opcode , condition_code , update_status , args) - super(opcode , condition_code , update_status , args) + def initialize(options) + super(options) + @update_status_flag = 0 + @condition_code = :al + @opcode = options[:opcode] + @args = [options[:left] , options[:right] ] + @operand = 0 + @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 + @rn = :r0 # register zero = zero bit pattern + @rd = :r0 # register zero = zero bit pattern end attr_accessor :i, :pre_post_index, :add_offset, :byte_access, :w, :is_load, :rn, :rd @@ -29,17 +35,17 @@ module Arm # Build representation for target address def build if( @is_load ) - @rd = args[0] - arg = args[1] + @rd = @args[0] + arg = @args[1] else #store - @rd = args[1] - arg = args[0] + @rd = @args[1] + arg = @args[0] end #str / ldr are _serious instructions. With BIG possibilities not half are implemented - if (arg.is_a?(Arm::Register)) + if (arg.is_a?(Symbol)) #symbol is register @rn = arg - if(arg.offset != 0) - @operand = arg.offset + if options[:offset] + @operand = options[:offset] if (@operand < 0) @add_offset = 0 #TODO test/check/understand @@ -51,6 +57,13 @@ module Arm raise "reference offset too large/small (max 4095) #{arg} #{inspect}" end end + elsif (arg.is_a?(Vm::StringLiteral)) #use pc relative + @rn = :pc + @operand = arg.position - self.position - 8 #stringtable is after code + @add_offset = 1 + if (@operand.abs > 4095) + raise "reference offset too large/small (max 4095) #{arg} #{inspect}" + end elsif (arg.is_a?(Arm::Label) or arg.is_a?(Arm::NumLiteral)) @pre_post_index = 1 @rn = pc @@ -69,9 +82,9 @@ module Arm @add_offset = 1 @pre_post_index = 1 instuction_class = 0b01 # OPC_MEMORY_ACCESS - val = operand - val |= (rd.bits << 12 ) - val |= (rn.bits << 12+4) #16 + val = @operand.is_a?(Symbol) ? reg_code(@operand) : @operand + val |= (reg_code(rd) << 12 ) + val |= (reg_code(rn) << 12+4) #16 val |= (is_load << 12+4 +4) val |= (w << 12+4 +4+1) val |= (byte_access << 12+4 +4+1+1) @@ -81,6 +94,7 @@ module Arm val |= (instuction_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 \ No newline at end of file diff --git a/lib/ast/basic_expressions.rb b/lib/ast/basic_expressions.rb index 9e9c1db3..5433798d 100644 --- a/lib/ast/basic_expressions.rb +++ b/lib/ast/basic_expressions.rb @@ -29,8 +29,9 @@ module Ast end def compile context - # TODO check if needst to be added? - Vm::ObjectReference.new( Vm::StringValue.new(string) ) + value = Vm::StringLiteral.new(string) + context.program.add_object value + value end def == other compare other , [:string] diff --git a/lib/ast/operator_expressions.rb b/lib/ast/operator_expressions.rb index 75a3d032..c8b4edab 100644 --- a/lib/ast/operator_expressions.rb +++ b/lib/ast/operator_expressions.rb @@ -12,8 +12,8 @@ module Ast fun = Vm::FunctionCall.new( name , args.collect{ |a| a.compile(context) } ) fun.assign_function context fun.load_args - #puts "funcall #{self.inspect}" fun.do_call + fun end def == other diff --git a/lib/core/kernel.rb b/lib/core/kernel.rb index f5e27631..080a27a3 100644 --- a/lib/core/kernel.rb +++ b/lib/core/kernel.rb @@ -2,7 +2,7 @@ module Core class Kernel #there are no Kernel instances, only class methods. - # We use this module syntax to avoid the (ugly) self. + # We use this module syntax to avoid the (ugly) self (also eases searching). module ClassMethods def main_start #TODO extract args into array of strings @@ -18,9 +18,9 @@ module Core def function_exit f_name Vm::Machine.instance.function_exit f_name end - def self.puts string + def putstring # should unwrap from string to char* - Vm::Machine.instance.puts string + Vm::Machine.instance.putstring end end diff --git a/lib/vm/block.rb b/lib/vm/block.rb index f4483648..d32cc8ec 100644 --- a/lib/vm/block.rb +++ b/lib/vm/block.rb @@ -56,12 +56,12 @@ module Vm obj.assemble io end end - + # set the next executed block after self. # why is this useful? if it's unconditional, why not merge them: # So the second block can be used as a jump target. You standard loop needs a block to setup # and at least one to do the calculation - def next block + def set_next block @next = block end diff --git a/lib/vm/code.rb b/lib/vm/code.rb index b56830ad..8864c2c6 100644 --- a/lib/vm/code.rb +++ b/lib/vm/code.rb @@ -21,7 +21,6 @@ module Vm # in other words, during assembly the position _must_ be resolved into a pc relative address # and not used as is def position - throw "Not set" unless @position @position end diff --git a/lib/vm/function.rb b/lib/vm/function.rb index b49c306f..feb751d2 100644 --- a/lib/vm/function.rb +++ b/lib/vm/function.rb @@ -6,49 +6,67 @@ module Vm # Functions also have arguments, though they are handled differently (in register allocation) - # Functions have a minimum of two blocks, entry and exit, which are created for you - # but there is no branch created between them, this must be done by the programmer. - + # Functions have a exactly three blocks, entry, exit and body, which are created for you + # with straight branches between them. - class Function < Block + # Also remember that if your den body exists of severa blocks, they must be wrapped in a + # block as the function really only has the one, and blocks only assemble their codes, + # not their next links + # This comes at zero runtime cost though, as the wrapper is just the sum of it's codes + + # If you change the body block to point elsewhere, remember to end up at exit + + class Function < Code def initialize(name , args = []) - super(name) + super() + @name = name @args = args @entry = Core::Kernel::function_entry( name ) @exit = Core::Kernel::function_exit( name ) + @body = Block.new("#{name}_body") + branch_body end - attr_reader :args , :entry , :exit - + attr_reader :args , :entry , :exit , :body , :name + + # this creates a branch from entry here and from here to exit + # unless there is a link existing, in which you are resposible + def set_body body + @body = body + branch_body + end + def arity @args.length end def link_at address , context -# function = context.program.get_function(name) -# unless function -# function = Core::Kernel.send(name) -# context.program.get_or_create_function( name , function , arity ) -# end - + raise "undefined code #{inspect}" if @body.nil? + super #just sets the position @entry.link_at address , context address += @entry.length - super(address , context) + @body.link_at(address , context) address += @entry.length @exit.link_at(address,context) end def length - @entry.length + @exit.length + super + @entry.length + @exit.length + @body.length end def assemble io - @entry.assemble io - super(io) + @entry.assemble(io) + @body.assemble(io) @exit.assemble(io) end - private + private + # set up the braches from entry to body and body to exit (unless that exists, see set_body) + def branch_body + @entry.set_next(@body) + @body.set_next(@exit) if @body and !@body.next + end + def add_arg value # TODO check @args << value diff --git a/lib/vm/function_call.rb b/lib/vm/function_call.rb index 67bbb9e7..51dd75ce 100644 --- a/lib/vm/function_call.rb +++ b/lib/vm/function_call.rb @@ -21,11 +21,12 @@ module Vm end def load_args args.each_with_index do |arg , index| - arg.load index + add_code arg.load(index) end end + def do_call - Machine.instance.function_call self + add_code Machine.instance.function_call self end end end diff --git a/lib/vm/program.rb b/lib/vm/program.rb index c0744b22..d02fead2 100644 --- a/lib/vm/program.rb +++ b/lib/vm/program.rb @@ -49,6 +49,8 @@ module Vm fun = get_function name unless fun fun = Function.new(name) + block = Core::Kernel.send(name) + fun.set_body block @functions << fun end fun diff --git a/lib/vm/string_literal.rb b/lib/vm/string_literal.rb deleted file mode 100644 index 704aecde..00000000 --- a/lib/vm/string_literal.rb +++ /dev/null @@ -1,30 +0,0 @@ -require "vm/code" - -module Vm - # The name really says it all. - # The only interesting thing is storage. - # Currently string are stored "inline" , ie in the code segment. - # Mainly because that works an i aint no elf expert. - - class StringLiteral < Vm::Code - - # currently aligned to 4 (ie padded with 0) and off course 0 at the end - def initialize(str) - length = str.length - # rounding up to the next 4 (always adding one for zero pad) - pad = ((length / 4 ) + 1 ) * 4 - length - raise "#{pad} #{self}" unless pad >= 1 - @string = str + "\x00" * pad - end - - # the strings length plus padding - def length - @string.length - end - - # just writing the string - def assemble(io) - io << @string - end - end -end \ No newline at end of file diff --git a/lib/vm/values.rb b/lib/vm/values.rb index 5bd71d39..83bdcfd4 100644 --- a/lib/vm/values.rb +++ b/lib/vm/values.rb @@ -41,40 +41,36 @@ module Vm end end - class Float < Word - end + # The name really says it all. + # The only interesting thing is storage. + # Currently string are stored "inline" , ie in the code segment. + # Mainly because that works an i aint no elf expert. - class Reference < Word - end - class StringValue < Value - def initialize string - @string = string - end - def length - @string.length + 3 + class StringLiteral < Value + + # currently aligned to 4 (ie padded with 0) and off course 0 at the end + def initialize(str) + super() + length = str.length + # rounding up to the next 4 (always adding one for zero pad) + pad = ((length / 4 ) + 1 ) * 4 - length + raise "#{pad} #{self}" unless pad >= 1 + @string = str + "\x00" * pad end attr_reader :string - end - - class MemoryReference < Reference - end - class ObjectReference < Reference - def initialize obj - @object = obj + def load reg_num + Machine.instance.string_load self , reg_num + end + + # the strings length plus padding + def length + @string.length end - attr_reader :object - def compiled context - if object.is_a? StringValue - context.program.add_object object - else - #TODO define object layout more generally and let objects lay themselves out - # as it is the program does this (in the objectwriter/stringtable) - un.done - end + # just writing the string + def assemble(io) + io << @string end end - end -require_relative "string_literal" \ No newline at end of file diff --git a/test/runners/hello.rb b/test/runners/hello.rb index f4e9ca1e..3af008ac 100644 --- a/test/runners/hello.rb +++ b/test/runners/hello.rb @@ -1 +1 @@ -puts( "hello world" ) +putstring( "Hello Raisa, I am crystksdfkljsncjncn" )