From 36f237c633858a80aed3580a1b316297367be2be Mon Sep 17 00:00:00 2001 From: Torsten Ruger Date: Sat, 7 Jun 2014 17:59:44 +0300 Subject: [PATCH] removed the (too) fancy dsl. Also introduce register indirection --- lib/arm/compare_instruction.rb | 2 +- lib/arm/constants.rb | 23 ++++++------ lib/arm/logic_instruction.rb | 4 +-- lib/arm/memory_instruction.rb | 6 ++-- lib/arm/move_instruction.rb | 11 +++--- lib/ast/call_site_expression.rb | 2 ++ lib/ast/function_expression.rb | 9 +++-- lib/ast/operator_expressions.rb | 5 ++- lib/ast/return_expression.rb | 4 +-- lib/boot/object.rb | 4 +-- lib/core/kernel.rb | 36 +++++++++---------- lib/support/hash_attributes.rb | 11 ------ lib/vm/block.rb | 41 +++------------------ lib/vm/boot_class.rb | 2 +- lib/vm/call_site.rb | 10 +++++- lib/vm/code.rb | 1 + lib/vm/constants.rb | 6 +++- lib/vm/function.rb | 22 ++++++------ lib/vm/instruction.rb | 12 +------ lib/vm/meta_class.rb | 2 +- lib/vm/register_machine.rb | 6 ++++ lib/vm/values.rb | 51 +++++++++++++-------------- test/fragments/test_functions.rb | 3 +- test/fragments/test_putint.rb | 4 +-- test/fragments/test_recursive_fibo.rb | 4 +-- test/fragments/test_while_fibo.rb | 8 ++--- 26 files changed, 128 insertions(+), 161 deletions(-) diff --git a/lib/arm/compare_instruction.rb b/lib/arm/compare_instruction.rb index e57c4dbe..cab31dbf 100644 --- a/lib/arm/compare_instruction.rb +++ b/lib/arm/compare_instruction.rb @@ -23,7 +23,7 @@ module Arm immediate = @immediate arg = @right - if arg.is_a?(Vm::StringConstant) + if arg.is_a?(Vm::ObjectConstant) # 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 = Vm::IntegerConstant.new( arg.position - self.position - 8 ) diff --git a/lib/arm/constants.rb b/lib/arm/constants.rb index 58ca65f9..97842bcf 100644 --- a/lib/arm/constants.rb +++ b/lib/arm/constants.rb @@ -50,20 +50,21 @@ module Arm 'a3' => 2, 'a4' => 3, 'v1' => 4, 'v2' => 5, 'v3' => 6, 'v4' => 7, 'v5' => 8, 'v6' => 9, 'rfp' => 9, 'sl' => 10, 'fp' => 11, 'ip' => 12, 'sp' => 13, 'lr' => 14, 'pc' => 15 } - def reg name - code = reg_code name - raise "no such register #{name}" unless code - Arm::Register.new(name.to_sym , code ) + def reg r_name + code = reg_code r_name + raise "no such register #{r_name}" unless code + Arm::Register.new(r_name.to_sym , code ) end - def reg_code name - if name.is_a? Vm::Word - name = "r#{name.register}" + def reg_code r_name + raise "double r #{r_name}" if( :rr1 == r_name) + if r_name.is_a? Vm::Word + r_name = r_name.register_symbol end - if name.is_a? Fixnum - name = "r#{name}" + if r_name.is_a? Fixnum + r_name = "r#{r_name}" end - r = REGISTERS[name.to_s] - raise "no reg #{name}" if r == nil + r = REGISTERS[r_name.to_s] + raise "no reg #{r_name}" if r == nil r end diff --git a/lib/arm/logic_instruction.rb b/lib/arm/logic_instruction.rb index 3fe897b2..c59629f7 100644 --- a/lib/arm/logic_instruction.rb +++ b/lib/arm/logic_instruction.rb @@ -25,7 +25,7 @@ module Arm immediate = @immediate right = @right - if @left.is_a?(Vm::StringConstant) + if @left.is_a?(Vm::ObjectConstant) # do pc relative addressing with the difference to the instuction # 8 is for the funny pipeline adjustment (ie pointing to fetch and not execute) right = @left.position - self.position - 8 @@ -36,7 +36,7 @@ module Arm right = Vm::IntegerConstant.new( right ) end if (right.is_a?(Vm::IntegerConstant)) - if (right.integer.fits_u8?) + if true #TODO (right.integer.fits_u8?) # no shifting needed operand = right.integer immediate = 1 diff --git a/lib/arm/memory_instruction.rb b/lib/arm/memory_instruction.rb index 5006ff9f..a01d51a0 100644 --- a/lib/arm/memory_instruction.rb +++ b/lib/arm/memory_instruction.rb @@ -29,14 +29,14 @@ module Arm add_offset = @add_offset arg = @left - arg = "r#{arg.register}".to_sym if( arg.is_a? Vm::Word ) + arg = arg.register_symbol if( arg.is_a? Vm::Word ) #str / ldr are _serious instructions. With BIG possibilities not half are implemented if (arg.is_a?(Symbol)) #symbol is register rn = arg if @right operand = @right #TODO better test, this operand integer (register) does not work. but sleep first - operand = operand.register if operand.is_a? Vm::Integer + operand = operand.register_symbol if operand.is_a? Vm::Integer unless( operand.is_a? Symbol) puts "operand #{operand.inspect}" if (operand < 0) @@ -51,7 +51,7 @@ module Arm end end end - elsif (arg.is_a?(Vm::StringConstant) ) #use pc relative + elsif (arg.is_a?(Vm::ObjectConstant) ) #use pc relative rn = :pc operand = arg.position - self.position - 8 #stringtable is after code add_offset = 1 diff --git a/lib/arm/move_instruction.rb b/lib/arm/move_instruction.rb index 6a57c0ee..28677448 100644 --- a/lib/arm/move_instruction.rb +++ b/lib/arm/move_instruction.rb @@ -12,10 +12,11 @@ module Arm @immediate = 0 @rn = :r0 # register zero = zero bit pattern -# raise inspect if to.is_a?(Vm::Value) and -# from.is_a?(Vm::Value) and -# !@attributes[:shift_lsr] and -# to.register == from.register + raise inspect if to.is_a?(Vm::Value) and + from.is_a?(Vm::Value) and + !@attributes[:shift_lsr] and + to.register_symbol == from.register_symbol + raise "uups " if @to.register_symbol == :rr1 end # arm intrucions are pretty sensible, and always 4 bytes (thumb not supported) @@ -30,7 +31,7 @@ module Arm immediate = @immediate right = @from - if right.is_a?(Vm::StringConstant) + if right.is_a?(Vm::ObjectConstant) # 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) right = Vm::IntegerConstant.new( right.position - self.position - 8 ) diff --git a/lib/ast/call_site_expression.rb b/lib/ast/call_site_expression.rb index b49d9085..67a44b38 100644 --- a/lib/ast/call_site_expression.rb +++ b/lib/ast/call_site_expression.rb @@ -9,6 +9,7 @@ module Ast if receiver.name == :self function = context.current_class.get_or_create_function(name) + value = Vm::Integer.new(Vm::Function::RECEIVER_REG) elsif receiver.is_a? ModuleName c_name = receiver.name clazz = context.object_space.get_or_create_class c_name @@ -23,6 +24,7 @@ module Ast else # should be case switch for basic tyes and dynamic dispatch for objects reference value = context.locals[receiver.name] + raise "no value" unless value function = context.current_class.get_or_create_function(name) end raise "No such method error #{clazz.to_s}:#{name}" if function == nil diff --git a/lib/ast/function_expression.rb b/lib/ast/function_expression.rb index cb4a9de0..837892f1 100644 --- a/lib/ast/function_expression.rb +++ b/lib/ast/function_expression.rb @@ -12,13 +12,12 @@ module Ast args << arg_value end # class depends on receiver + me = Vm::Integer.new( Vm::Function::RECEIVER_REG ) if receiver.nil? clazz = context.current_class - me = Vm::Integer.new( Vm::Function::RECEIVER_REG ) else c = context.object_space.get_or_create_class receiver.name.to_sym clazz = c.meta_class - raise "get the constant loaded to 1" end function = Vm::Function.new(name , me , args ) @@ -38,10 +37,10 @@ module Ast end return_reg = Vm::Integer.new(7) - if last_compiled.is_a?(Vm::IntegerConstant) or last_compiled.is_a?(Vm::StringConstant) - return_reg.load into , last_compiled if last_compiled.register != return_reg.register + if last_compiled.is_a?(Vm::IntegerConstant) or last_compiled.is_a?(Vm::ObjectConstant) + return_reg.load into , last_compiled if last_compiled.register_symbol != return_reg.register_symbol else - return_reg.move( into, last_compiled ) if last_compiled.register != return_reg.register + return_reg.move( into, last_compiled ) if last_compiled.register_symbol != return_reg.register_symbol end function.set_return return_reg diff --git a/lib/ast/operator_expressions.rb b/lib/ast/operator_expressions.rb index 45c2abaa..54a09c83 100644 --- a/lib/ast/operator_expressions.rb +++ b/lib/ast/operator_expressions.rb @@ -18,8 +18,7 @@ module Ast end l_val = left.compile(context , into) - into = into.scope binding - + case operator when ">" code = l_val.greater_than into , r_val @@ -33,7 +32,7 @@ module Ast code = l_val.equals into , r_val when "+" res = context.function.new_local - into.res = l_val + r_val + into.add res , l_val , r_val code = res when "-" res = context.function.new_local diff --git a/lib/ast/return_expression.rb b/lib/ast/return_expression.rb index 4efd6d10..8c4afbff 100644 --- a/lib/ast/return_expression.rb +++ b/lib/ast/return_expression.rb @@ -7,10 +7,10 @@ module Ast # copied from function expression: TODO make function return_reg = Vm::Integer.new(7) - if expression_value.is_a?(Vm::IntegerConstant) or expression_value.is_a?(Vm::StringConstant) + if expression_value.is_a?(Vm::IntegerConstant) or expression_value.is_a?(Vm::ObjectConstant) return_reg.load into , expression_value else - return_reg.move( into, expression_value ) if expression_value.register != return_reg.register + return_reg.move( into, expression_value ) if expression_value.register_symbol != return_reg.register_symbol end #function.set_return return_reg return return_reg diff --git a/lib/boot/object.rb b/lib/boot/object.rb index feddfde9..9c99652d 100644 --- a/lib/boot/object.rb +++ b/lib/boot/object.rb @@ -19,8 +19,8 @@ module Boot # The at_index is just "below" the api, somehting we need but don't want to expose, so we can't code the above in ruby def _get_instance_variable context , name = Vm::Integer get_function = Vm::Function.new(:_get_instance_variable , Vm::Integer , [ Vm::Integer ] , Vm::Integer ) - me = get_function.args[0] - var_name = get_function.args[1] + me = get_function.receiver + var_name = get_function.args.first return_to = get_function.return_type index_function = context.object_space.get_or_create_class(:Object).get_or_create_function(:index_of) b = get_function.body diff --git a/lib/core/kernel.rb b/lib/core/kernel.rb index 1a17c333..c0d55c4f 100644 --- a/lib/core/kernel.rb +++ b/lib/core/kernel.rb @@ -36,7 +36,7 @@ module Core putint_function = Vm::Function.new(:putint , Vm::Integer , [] , Vm::Integer ) buffer = Vm::StringConstant.new(" ") # create a buffer context.object_space.add_object buffer # and save it (function local variable: a no no) - int = putint_function.args.first + int = putint_function.receiver moved_int = putint_function.new_local utoa = context.object_space.get_or_create_class(:Object).get_or_create_function(:utoa) b = putint_function.body @@ -44,7 +44,7 @@ module Core #b.a buffer => int # string to write to b.add( int , buffer ,nil ) # string to write to - b.a int + (buffer.length-3) => int + b.add(int , int , buffer.length - 3) b.call( utoa ) # And now we "just" have to print it, using the write_stdout b.add( int , buffer , nil ) # string to write to @@ -60,13 +60,13 @@ module Core # arguments: string address , integer def utoa context utoa_function = Vm::Function.new(:utoa , Vm::Integer , [ Vm::Integer ] , Vm::Integer ) - str_addr = utoa_function.args[0] - number = utoa_function.args[1] + str_addr = utoa_function.receiver + number = utoa_function.args.first remainder = utoa_function.new_local Vm::RegisterMachine.instance.div10( utoa_function.body , number , remainder ) # make char out of digit (by using ascii encoding) 48 == "0" - b = utoa_function.body.scope binding - b.remainder = remainder + 48 + b = utoa_function.body + b.add(remainder , remainder , 48) b.strb( remainder, str_addr ) b.sub( str_addr, str_addr , 1 ) b.cmp( number , 0 ) @@ -80,26 +80,26 @@ module Core # not my hand off course, found in the net from a basic introduction def fibo context fibo_function = Vm::Function.new(:fibo , Vm::Integer , [] , Vm::Integer ) - result = Vm::Integer.new(7) - int = fibo_function.args[0] + result = fibo_function.return_type + int = fibo_function.receiver count = fibo_function.new_local f1 = fibo_function.new_local f2 = fibo_function.new_local - b = fibo_function.body.scope binding + b = fibo_function.body - b.a int == 1 + b.cmp int , 1 b.mov( result, int , condition_code: :le) b.mov( :pc , :lr , condition_code: :le) b.push [ count , f1 , f2 , :lr] - b.f1 = 1 - b.f2 = 0 - b.count = int - 2 + b.mov f1 , 1 + b.mov f2 , 0 + b.sub count , int , 2 - l = fibo_function.body.new_block("loop").scope binding - - l.f1 = f1 + f2 - l.f2 = f1 - f2 - l.count = (count - 1).set_update_status + l = fibo_function.body.new_block("loop") + + l.add f1 , f1 , f2 + l.sub f2 , f1 , f2 + l.sub count , count , 1 , set_update_status: 1 l.bpl( l ) l.mov( result , f1 ) fibo_function.set_return result diff --git a/lib/support/hash_attributes.rb b/lib/support/hash_attributes.rb index 0114d1ac..dd1cb301 100644 --- a/lib/support/hash_attributes.rb +++ b/lib/support/hash_attributes.rb @@ -24,14 +24,3 @@ module Support end end end - -class Binding - #these are defined in 2.1 and thus the definitions should be conditional. TODO - def local_variable_defined? sym - vars = eval("local_variables") - vars.include? sym - end - def local_variable_get sym - eval(sym.to_s) - end -end diff --git a/lib/vm/block.rb b/lib/vm/block.rb index 852c6a26..8473940d 100644 --- a/lib/vm/block.rb +++ b/lib/vm/block.rb @@ -32,14 +32,8 @@ module Vm attr_reader :name , :next , :codes , :function def add_code(kode) - if kode.is_a? Hash - raise "Hack only for 1 element #{inspect} #{kode.inspect}" unless kode.length == 1 - instruction , result = kode.first - instruction.assign result - kode = instruction - end raise "alarm #{kode}" if kode.is_a? Word - raise "alarm #{kode}" unless kode.is_a? Code + raise "alarm #{kode.class} #{kode}" unless kode.is_a? Code @insert_at.codes << kode self end @@ -70,27 +64,8 @@ module Vm self end - # to use the assignment syntax (see method_missing) the scope must be set, so variables can be resolved - # The scope you set should be a binding (literally, the kernel.binding) - # The function return the block, so it can be chained into an assignment - # Example (coding a function ) and having variable int defined - # b = function.body.scope(binding) - # b.int = 5 will create a mov instruction to set the register that int points to - def scope where - @scope = where - self - end - - # sugar to create instructions easily. Actually just got double sweet with two versions: - # 1 for any method that ends in = we evaluate the method name in the current scope (see scope()) - # for the result we call assign with the right value. The resulting instruction is added to - # the block. - # Thus we emulate assignment, - # Example: block b - # b.variable = value looks like what it does, but actually generates - # an instruction for the block (mov or add) - # - # 2- any other method will be passed on to the RegisterMachine and the result added to the block + # sugar to create instructions easily. + # any method will be passed on to the RegisterMachine and the result added to the block # With this trick we can write what looks like assembler, # Example b.instance_eval # mov( r1 , r2 ) @@ -98,16 +73,8 @@ module Vm # end # mov and add will be called on Machine and generate Inststuction that are then added # to the block + # also symbols are supported and wrapped as register usages (for bare metal programming) def method_missing(meth, *args, &block) - var = meth.to_s[0 ... -1] - if( args.length == 1) and ( meth.to_s[-1] == "=" ) - if @scope.local_variable_defined? var.to_sym - l_val = @scope.local_variable_get var.to_sym - return add_code l_val.assign(args[0]) - else - return super - end - end add_code RegisterMachine.instance.send(meth , *args) end diff --git a/lib/vm/boot_class.rb b/lib/vm/boot_class.rb index 33a8ab03..7bb0705e 100644 --- a/lib/vm/boot_class.rb +++ b/lib/vm/boot_class.rb @@ -3,7 +3,7 @@ require_relative "meta_class" module Vm # class is mainly a list of functions with a name (for now) # layout of object is seperated into Layout - class BootClass < Code + class BootClass < ObjectConstant def initialize name , context , super_class = :Object super() @context = context diff --git a/lib/vm/call_site.rb b/lib/vm/call_site.rb index d6111b68..8bb8e002 100644 --- a/lib/vm/call_site.rb +++ b/lib/vm/call_site.rb @@ -9,15 +9,23 @@ module Vm @value = value @args = args @function = function + raise "oh #{name} " unless value end attr_reader :function , :args , :name , :value def load_args into + if value.is_a?(IntegerConstant) or value.is_a?(ObjectConstant) + function.receiver.load into , value + else + raise "meta #{name} " if value.is_a? MetaClass + function.receiver.move( into, value ) if value.register_symbol != function.receiver.register_symbol + end + raise "function call '#{name}' has #{args.length} arguments, but function has #{function.args.length}" if args.length != function.args.length args.each_with_index do |arg , index| if arg.is_a?(IntegerConstant) or arg.is_a?(StringConstant) function.args[index].load into , arg else - function.args[index].move( into, arg ) if arg.register != function.args[index].register + function.args[index].move( into, arg ) if arg.register_symbol != function.args[index].register_symbol end end end diff --git a/lib/vm/code.rb b/lib/vm/code.rb index 869756ff..54cc062d 100644 --- a/lib/vm/code.rb +++ b/lib/vm/code.rb @@ -43,5 +43,6 @@ module Vm def assemble(io) raise "Not implemented #{self.inspect}" end + end end \ No newline at end of file diff --git a/lib/vm/constants.rb b/lib/vm/constants.rb index 12e10907..ae0bf929 100644 --- a/lib/vm/constants.rb +++ b/lib/vm/constants.rb @@ -9,6 +9,10 @@ module Vm end + # another abstract "marker" class (so we can check for it) + # derived classes are Boot/Meta Clas and StringConstant + class ObjectConstant < Constant + end class IntegerConstant < Constant def initialize int @@ -25,7 +29,7 @@ module Vm # Currently string are stored "inline" , ie in the code segment. # Mainly because that works an i aint no elf expert. - class StringConstant < Constant + class StringConstant < ObjectConstant attr_reader :string # currently aligned to 4 (ie padded with 0) and off course 0 at the end def initialize str diff --git a/lib/vm/function.rb b/lib/vm/function.rb index 01f9e2d4..96787322 100644 --- a/lib/vm/function.rb +++ b/lib/vm/function.rb @@ -27,16 +27,16 @@ module Vm class Function < Code - TYPE_REG = 0 - RECEIVER_REG = 1 - RETURN_REG = 7 + TYPE_REG = :r0 + RECEIVER_REG = :r1 + RETURN_REG = :r7 def initialize(name , receiver = Vm::Integer , args = [] , return_type = Vm::Integer) super() @name = name.to_sym if receiver.is_a?(Value) @receiver = receiver - raise "arg in non std register #{arg.inspect}" unless RECEIVER_REG == receiver.register + raise "arg in non std register #{arg.inspect}" unless RECEIVER_REG == receiver.register_symbol else @receiver = receiver.new(RECEIVER_REG) end @@ -45,9 +45,9 @@ module Vm args.each_with_index do |arg , i| if arg.is_a?(Value) @args[i] = arg - raise "arg in non std register #{arg.inspect}" unless (i+1+RECEIVER_REG) == arg.register + raise "arg in non std register #{arg.inspect}" unless RECEIVER_REG == arg.used_register.next_reg(i - 1) else - @args[i] = arg.new(i+1+RECEIVER_REG) + @args[i] = arg.new(RegisterUse.new(RECEIVER_REG).next_reg(i + 1)) end end set_return return_type @@ -59,12 +59,12 @@ module Vm @blocks = [] end - attr_reader :args , :entry , :exit , :body , :name , :return_type , :revceiver + attr_reader :args , :entry , :exit , :body , :name , :return_type , :receiver def set_return type_or_value @return_type = type_or_value || Vm::Integer if @return_type.is_a?(Value) - raise "return in non std register #{@return_type.inspect}" unless RETURN_REG == @return_type.register + raise "return in non std register #{@return_type.inspect}" unless RETURN_REG == @return_type.register_symbol else @return_type = @return_type.new(RETURN_REG) end @@ -76,7 +76,7 @@ module Vm def new_local type = Vm::Integer register = args.length + 1 + @locals.length # one for the receiver implicit arg l = type.new(register + 1) # one for the type register 0, TODO add type as arg0 implicitly - puts "new local #{l.register}" + puts "new local #{l.register_symbol}" # raise "Register overflow in function #{name}" if l.register > 6 @locals << l l @@ -84,13 +84,13 @@ module Vm #BUG - must save receiver def save_locals context , into - save = args.collect{|a| a.register } + @locals.collect{|l| l.register} + save = args.collect{|a| a.register_symbol } + @locals.collect{|l| l.register_symbol} into.push(save) unless save.empty? end def restore_locals context , into #TODO assumes allocation in order, as the pop must be get regs in ascending order (also push) - restore = args.collect{|a| a.register } + @locals.collect{|l| l.register} + restore = args.collect{|a| a.register_symbol } + @locals.collect{|l| l.register_symbol} into.pop(restore) unless restore.empty? end diff --git a/lib/vm/instruction.rb b/lib/vm/instruction.rb index 019d88e9..7b5e390e 100644 --- a/lib/vm/instruction.rb +++ b/lib/vm/instruction.rb @@ -68,16 +68,6 @@ module Vm @right = right super(options) end - # this is used to write code that looks like assignment - # So instructions can be created without the result (register) set, and this assigns where - # the reuslt after the fact, but usually in the same line - # Example (with block b, and variables int,a,b): b.int = a + b - # a + b actually creates an add instruction while the b.int= assigns the result to int - # b.add( int , a , b) is an alternative (assmbler style) way of writing the same. - def assign left - @result = left - self - end end class MathInstruction < Instruction def initialize first , options = {} @@ -96,7 +86,7 @@ module Vm def initialize to , from , options = {} @to = to @from = from - raise inspect unless from + raise "move must have from set #{inspect}" unless from super(options) end end diff --git a/lib/vm/meta_class.rb b/lib/vm/meta_class.rb index 12514d3f..17efffeb 100644 --- a/lib/vm/meta_class.rb +++ b/lib/vm/meta_class.rb @@ -8,7 +8,7 @@ module Vm # PS: can't say i fancy the << self syntax and am considerernig adding a keyword for it, like meta # In effect it is a very similar construct to def self.function(...) # So one could write def meta.function(...) and thus define on the meta-class - class MetaClass < Code + class MetaClass < ObjectConstant # no name, nor nothing. as this is just the object really def initialize(object) diff --git a/lib/vm/register_machine.rb b/lib/vm/register_machine.rb index b5dcb49e..f8979396 100644 --- a/lib/vm/register_machine.rb +++ b/lib/vm/register_machine.rb @@ -108,6 +108,7 @@ module Vm options = {} if options == nil options.merge defaults options[:opcode] = inst + first = Vm::Integer.new(first) if first.is_a? Symbol clazz.new(first , options) end end @@ -118,6 +119,8 @@ module Vm create_method(inst) do |left ,right , options = nil| options = {} if options == nil options.merge defaults + left = Vm::Integer.new(left) if left.is_a? Symbol + right = Vm::Integer.new(right) if right.is_a? Symbol options[:opcode] = inst clazz.new(left , right ,options) end @@ -130,6 +133,9 @@ module Vm options = {} if options == nil options.merge defaults options[:opcode] = inst + result = Vm::Integer.new(result) if result.is_a? Symbol + left = Vm::Integer.new(left) if left.is_a? Symbol + right = Vm::Integer.new(right) if right.is_a? Symbol clazz.new(result, left , right ,options) end end diff --git a/lib/vm/values.rb b/lib/vm/values.rb index 982c6307..0f318347 100644 --- a/lib/vm/values.rb +++ b/lib/vm/values.rb @@ -53,9 +53,23 @@ module Vm # but which register can be changed, and _all_ instructions sharing the RegisterUse then use that register # In other words a simple level of indirection, or change from value to reference sematics. class RegisterUse - attr_accessor :register + attr_accessor :symbol def initialize r - @register = r + if( r.is_a? Fixnum) + r = "r#{r}".to_sym + end + raise "wrong type for register init #{r}" unless r.is_a? Symbol + raise "double r #{r}" if r == :rr1 + @symbol = r + end + + #helper methods to calculate with register symbols + def next_reg by = 1 + int = @symbol[1,3].to_i + "r#{int + by}".to_sym + end + def next_reg_use by = 1 + RegisterUse.new( next_reg(by) ) end end @@ -66,23 +80,21 @@ module Vm attr_accessor :used_register - def register - @used_register.register + def register_symbol + @used_register.symbol end - def inspect - self.class.name + "(r#{used_register.register})" + "#{self.class.name} (#{register_symbol})" end def to_s inspect end def initialize reg - if reg.is_a? Fixnum - @used_register = RegisterUse.new(reg) - else + if reg.is_a? RegisterUse @used_register = reg + else + @used_register = RegisterUse.new(reg) end - raise inspect if reg == nil end def length 4 @@ -98,21 +110,6 @@ module Vm class Integer < Word - # part of the dsl. - # Gets called with either fixnum/IntegerConstant or an Instruction (usually logic, iw add...) - # For instructions we flip, ie call the assign on the instruction - # but for constants we have to create instruction first (mov) - def assign other - other = Vm::IntegerConstant.new(other) if other.is_a? Fixnum - if other.is_a?(Vm::IntegerConstant) or other.is_a?(Vm::Integer) - class_for(MoveInstruction).new( self , other , :opcode => :mov) - elsif other.is_a?(Vm::StringConstant) # pc relative addressing - class_for(LogicInstruction).new(self , other , nil , opcode: :add) - else - other.assign(self) - end - end - def less_or_equal block , right RegisterMachine.instance.integer_less_or_equal block , self , right end @@ -155,7 +152,9 @@ module Vm block.mov( self , right ) #move the value elsif right.is_a? StringConstant block.add( self , right , nil) #move the address, by "adding" to pc, ie pc relative - block.mov( Integer.new(register+1) , right.length ) #and the length HACK TODO + block.mov( Integer.new(self.used_register.next_reg) , right.length ) #and the length HACK TODO + elsif right.is_a?(BootClass) or right.is_a?(MetaClass) + block.add( self , right , nil) #move the address, by "adding" to pc, ie pc relative else raise "unknown #{right.inspect}" end diff --git a/test/fragments/test_functions.rb b/test/fragments/test_functions.rb index a0c845b2..15877083 100644 --- a/test/fragments/test_functions.rb +++ b/test/fragments/test_functions.rb @@ -21,7 +21,8 @@ def minus(a,b) end end -putint(times(7,6)) +tim = times(7,6) +tim.putint() HERE @should = [0x0,0x40,0x2d,0xe9,0x2,0x30,0x41,0xe0,0x3,0x70,0xa0,0xe1,0x0,0x80,0xbd,0xe8,0x0,0x40,0x2d,0xe9,0x2,0x30,0x41,0xe0,0x3,0x70,0xa0,0xe1,0x0,0x80,0xbd,0xe8] @output = " 42 " diff --git a/test/fragments/test_putint.rb b/test/fragments/test_putint.rb index 8d26ebe8..b90f81f1 100644 --- a/test/fragments/test_putint.rb +++ b/test/fragments/test_putint.rb @@ -5,7 +5,8 @@ class TestPutint < MiniTest::Test def test_putint @string_input = <