work on Integer.to_s , not a simple task as it turns out

This commit is contained in:
Torsten Ruger 2014-05-15 16:54:23 +03:00
parent 918ede1c02
commit b4c79d218f
12 changed files with 80 additions and 11 deletions

View File

@ -72,8 +72,57 @@ module Arm
syscall( block , 4 ) syscall( block , 4 )
end 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 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 def syscall block , num
block.add_code mov( :left => :r7 , :right => num ) block.add_code mov( :left => :r7 , :right => num )
block.add_code swi( :left => 0 ) block.add_code swi( :left => 0 )

View File

@ -57,7 +57,8 @@ module Arm
if (@operand.abs > 4095) if (@operand.abs > 4095)
raise "reference offset too large/small (max 4095) #{arg} #{inspect}" raise "reference offset too large/small (max 4095) #{arg} #{inspect}"
end 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 @pre_post_index = 1
@rn = pc @rn = pc
@use_addrtable_reloc = true @use_addrtable_reloc = true

View File

@ -23,7 +23,7 @@ module Core
#TODO this is in the wrong place. It is a function that returns a function object #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 # 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 ] ) function = Vm::Function.new(:putstring , [Vm::Integer , Vm::Integer ] )
block = function.body block = function.body
# should be another level of indirection, ie write(io,str) # should be another level of indirection, ie write(io,str)
@ -31,6 +31,17 @@ module Core
function.return_type = ret function.return_type = ret
function function
end 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 end
extend ClassMethods extend ClassMethods

View File

@ -3,7 +3,7 @@ require 'parslet'
require "elf/object_writer" require "elf/object_writer"
require 'parser/crystal' require 'parser/crystal'
require 'parser/transform' require 'parser/transform'
require "vm/context"
require "vm/c_machine" require "vm/c_machine"
require "vm/program" require "vm/program"
require "stream_reader" require "stream_reader"
require "core/kernel"

View File

@ -35,6 +35,7 @@ module Vm
def add_code(kode) def add_code(kode)
raise "alarm #{kode}" if kode.is_a? Word raise "alarm #{kode}" if kode.is_a? Word
raise "alarm #{kode}" unless kode.is_a? Code
@codes << kode @codes << kode
self self
end end

View File

@ -66,6 +66,7 @@ module Vm
# code, and has opcode set to :b and :condition_code set to the condition # code, and has opcode set to :b and :condition_code set to the condition
CONDITIONS.each do |suffix| CONDITIONS.each do |suffix|
define_instruction_for("b#{suffix}".to_sym , CallInstruction) define_instruction_for("b#{suffix}".to_sym , CallInstruction)
define_instruction_for("call#{suffix}".to_sym , CallInstruction)
end end
end end

View File

@ -1,6 +1,4 @@
require "core/kernel" require "support/hash_attributes"
require_relative "program"
module Vm module Vm
#currently just holding the program in here so we can have global access #currently just holding the program in here so we can have global access

View File

@ -9,7 +9,7 @@ module Vm
# Functions have a exactly three blocks, entry, exit and body, which are created for you # Functions have a exactly three blocks, entry, exit and body, which are created for you
# with straight branches between them. # 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, # block as the function really only has the one, and blocks only assemble their codes,
# not their next links # not their next links
# This comes at zero runtime cost though, as the wrapper is just the sum of it's codes # This comes at zero runtime cost though, as the wrapper is just the sum of it's codes

View File

@ -50,6 +50,10 @@ module Vm
@attributes[:condition_code] = opcode[1,2].to_sym @attributes[:condition_code] = opcode[1,2].to_sym
@attributes[:opcode] = :b @attributes[:opcode] = :b
end end
if opcode.length == 6 and opcode[0] == "c"
@attributes[:condition_code] = opcode[4,2].to_sym
@attributes[:opcode] = :call
end
end end
end end
end end

View File

@ -1,6 +1,8 @@
require_relative "context"
require_relative "function" require_relative "function"
require_relative "call_site" require_relative "call_site"
require "arm/arm_machine" require "arm/arm_machine"
require "core/kernel"
module Vm module Vm
# A Program represents an executable that we want to build # A Program represents an executable that we want to build
@ -58,7 +60,7 @@ module Vm
def get_or_create_function name def get_or_create_function name
fun = get_function name fun = get_function name
unless fun unless fun
fun = Core::Kernel.send(name) fun = Core::Kernel.send(name , context)
raise "no such function '#{name}'" if fun == nil raise "no such function '#{name}'" if fun == nil
@functions << fun @functions << fun
end end

View File

@ -5,7 +5,8 @@ def fibonaccit(n) # n == r0
tmp = a # r3 <- r1 tmp = a # r3 <- r1
a = b # r1 <- r2 a = b # r1 <- r2
b = tmp + b # r4 = r2 + r3 (r4 transient) r2 <- r4 b = tmp + b # r4 = r2 + r3 (r4 transient) r2 <- r4
putstring(b) tmp = inttos(b)
putstring(tmp)
n = n - 1 # r2 <- 0 ???? #call ok n = n - 1 # r2 <- 0 ???? #call ok
end #r5 <- r0 - 1 # r0 <- r5 end #r5 <- r0 - 1 # r0 <- r5
end end

1
test/runners/putint.rb Normal file
View File

@ -0,0 +1 @@
putint( 42 )