diff --git a/lib/arm/arm_machine.rb b/lib/arm/arm_machine.rb index 7728e261..c76ce9c4 100644 --- a/lib/arm/arm_machine.rb +++ b/lib/arm/arm_machine.rb @@ -72,8 +72,57 @@ module Arm syscall( block , 4 ) end + # make a string out of the integer. + # as we don't have memory manegement yet, you have to pass the string in (ouch) + # in a weird twist the string is actually a string, while we actually use its address. + def integer_to_s block , string + number = Vm::Integer.new(0) + tos = Vm::Block.new("integer_to_s") # need to create a block to jump to + block.add_code(tos) # and then use the new block to add code + #STMFD sp!, {r9, r10, lr} #function entry save working regs (for recursion) + tos.add_code( push :regs => [:lr ]) #and the return address. + # MOV r9, r1 # preserve arguments over following + # MOV r10, r2 # function calls + # pin data, ie no saving + remainder = Vm::Integer.new( number.register + 2) + # BL udiv10 # r1 = r1 / 10 + div10( tos , number , remainder ) + # ADD r10, r10, 48 #'0' # make char out of digit (by using ascii encoding) + tos.add_code( add( left: remainder , right: remainder , extra: 48 )) + #STRB r10, [r1], 1 # store digit at end of buffer + tos.add_code( strb( left: string , right: remainder )) #and increment TODO check + # CMP r1, #0 # quotient non-zero? + tos.add_code( cmp left: number , right: 0 ) + #BLNE utoa # conditional recursive call to utoa + tos.add_code( callne( left: tos )) + #LDMFD sp!, {r9, r10, pc} # function exit - restore and return + tos.add_code( pop regs: [:pc]) + end + private + + # the number (a Vm::integer) is (itself) divided by 10, ie overwritten by the result + # and the remainder is overwritten (ie an out argument) + # not really a function, more a macro, hence private + def div10 block, number , remainder + + # takes argument in r1 + # returns quotient in r1, remainder in r2 + # SUB r2, r1, #10 # keep (x-10) for later + block.add_code sub(:left => remainder , :right => number , :extra => 10 ) +# SUB r1, r1, r1, lsr #2 +# ADD r1, r1, r1, lsr #4 +# ADD r1, r1, r1, lsr #8 +# ADD r1, r1, r1, lsr #16 +# MOV r1, r1, lsr #3 +# ADD r3, r1, r1, asl #2 +# SUBS r2, r2, r3, asl #1 # calc (x-10) - (x/10)*10 +# ADDPL r1, r1, #1 # fix-up quotient +# ADDMI r2, r2, #10 # fix-up remainder +# MOV pc, lr + end + def syscall block , num block.add_code mov( :left => :r7 , :right => num ) block.add_code swi( :left => 0 ) diff --git a/lib/arm/memory_instruction.rb b/lib/arm/memory_instruction.rb index febe4606..0225ffd6 100644 --- a/lib/arm/memory_instruction.rb +++ b/lib/arm/memory_instruction.rb @@ -50,14 +50,15 @@ module Arm raise "reference offset too large/small (max 4095) #{arg} #{inspect}" end end - elsif (arg.is_a?(Vm::StringConstant)) #use pc relative + elsif (arg.is_a?(Vm::StringConstant) ) #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?(Vm::IntegerConstant)) + elsif( arg.is_a?(Vm::IntegerConstant) ) + raise "is this working ?? #{arg} #{inspect}" @pre_post_index = 1 @rn = pc @use_addrtable_reloc = true diff --git a/lib/core/kernel.rb b/lib/core/kernel.rb index 4f707b94..ae2f3879 100644 --- a/lib/core/kernel.rb +++ b/lib/core/kernel.rb @@ -23,7 +23,7 @@ module Core #TODO this is in the wrong place. It is a function that returns a function object # while all other methods add their code into some block. --> kernel - def putstring + def putstring context function = Vm::Function.new(:putstring , [Vm::Integer , Vm::Integer ] ) block = function.body # should be another level of indirection, ie write(io,str) @@ -31,6 +31,17 @@ module Core function.return_type = ret function end + + def putint context + function = Vm::Function.new(:putint , [Vm::Integer , Vm::Integer ] ) + block = function.body + buffer = Vm::StringConstant.new(" ") + context.program.add_object buffer + # should be another level of indirection, ie write(io,str) + ret = Vm::CMachine.instance.integer_to_s(block , buffer) + function.return_type = ret + function + end end extend ClassMethods diff --git a/lib/crystal.rb b/lib/crystal.rb index d007f2b6..aed5477a 100644 --- a/lib/crystal.rb +++ b/lib/crystal.rb @@ -3,7 +3,7 @@ require 'parslet' require "elf/object_writer" require 'parser/crystal' require 'parser/transform' -require "vm/context" require "vm/c_machine" require "vm/program" -require "stream_reader" \ No newline at end of file +require "stream_reader" +require "core/kernel" diff --git a/lib/vm/block.rb b/lib/vm/block.rb index dc76ea1e..b4eed6d3 100644 --- a/lib/vm/block.rb +++ b/lib/vm/block.rb @@ -35,6 +35,7 @@ module Vm def add_code(kode) raise "alarm #{kode}" if kode.is_a? Word + raise "alarm #{kode}" unless kode.is_a? Code @codes << kode self end diff --git a/lib/vm/c_machine.rb b/lib/vm/c_machine.rb index cb376ad6..cab64c5e 100644 --- a/lib/vm/c_machine.rb +++ b/lib/vm/c_machine.rb @@ -66,6 +66,7 @@ module Vm # code, and has opcode set to :b and :condition_code set to the condition CONDITIONS.each do |suffix| define_instruction_for("b#{suffix}".to_sym , CallInstruction) + define_instruction_for("call#{suffix}".to_sym , CallInstruction) end end diff --git a/lib/vm/context.rb b/lib/vm/context.rb index 1b6c679b..47acc1ee 100644 --- a/lib/vm/context.rb +++ b/lib/vm/context.rb @@ -1,6 +1,4 @@ -require "core/kernel" -require_relative "program" - +require "support/hash_attributes" module Vm #currently just holding the program in here so we can have global access diff --git a/lib/vm/function.rb b/lib/vm/function.rb index 70b7f107..9ce5cc62 100644 --- a/lib/vm/function.rb +++ b/lib/vm/function.rb @@ -9,7 +9,7 @@ module Vm # Functions have a exactly three blocks, entry, exit and body, which are created for you # with straight branches between them. - # Also remember that if your den body exists of severa blocks, they must be wrapped in a + # Also remember that if your den body exists of several 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 diff --git a/lib/vm/instruction.rb b/lib/vm/instruction.rb index 26169ded..b87383b8 100644 --- a/lib/vm/instruction.rb +++ b/lib/vm/instruction.rb @@ -50,6 +50,10 @@ module Vm @attributes[:condition_code] = opcode[1,2].to_sym @attributes[:opcode] = :b end + if opcode.length == 6 and opcode[0] == "c" + @attributes[:condition_code] = opcode[4,2].to_sym + @attributes[:opcode] = :call + end end end end diff --git a/lib/vm/program.rb b/lib/vm/program.rb index b84c79d6..ce5599e9 100644 --- a/lib/vm/program.rb +++ b/lib/vm/program.rb @@ -1,6 +1,8 @@ +require_relative "context" require_relative "function" require_relative "call_site" require "arm/arm_machine" +require "core/kernel" module Vm # A Program represents an executable that we want to build @@ -58,7 +60,7 @@ module Vm def get_or_create_function name fun = get_function name unless fun - fun = Core::Kernel.send(name) + fun = Core::Kernel.send(name , context) raise "no such function '#{name}'" if fun == nil @functions << fun end diff --git a/test/runners/fibo_while.rb b/test/runners/fibo_while.rb index 68ac73cb..e9a1d035 100644 --- a/test/runners/fibo_while.rb +++ b/test/runners/fibo_while.rb @@ -5,7 +5,8 @@ def fibonaccit(n) # n == r0 tmp = a # r3 <- r1 a = b # r1 <- r2 b = tmp + b # r4 = r2 + r3 (r4 transient) r2 <- r4 - putstring(b) + tmp = inttos(b) + putstring(tmp) n = n - 1 # r2 <- 0 ???? #call ok end #r5 <- r0 - 1 # r0 <- r5 end diff --git a/test/runners/putint.rb b/test/runners/putint.rb new file mode 100644 index 00000000..2451e950 --- /dev/null +++ b/test/runners/putint.rb @@ -0,0 +1 @@ +putint( 42 )