diff --git a/lib/parfait.rb b/lib/parfait.rb index b2664718..30419df2 100644 --- a/lib/parfait.rb +++ b/lib/parfait.rb @@ -3,6 +3,7 @@ module Parfait end require_relative "parfait/object" +require_relative "parfait/factory" require_relative "parfait/data_object" require_relative "parfait/integer" require_relative "parfait/behaviour" diff --git a/lib/parfait/factory.rb b/lib/parfait/factory.rb new file mode 100644 index 00000000..fb38c37c --- /dev/null +++ b/lib/parfait/factory.rb @@ -0,0 +1,117 @@ +module Parfait + # A factory has the one job of handing out new instances + # + # A factory is for a specific type (currently, may change by size at some point) + # + # get_next_object is the main entry point, all other functions help to get more + # memory and objects as needed + # + # A factory keeps a reserve, and in case the freelist is empty, switches that in _immediately + # This is especially useful for messages, that can then be used even they run out. + # + # The idea (especially for messages) is to call out from the MessageSetup to the + # factory when the next (not current) is nil. + # This is btw just as easy a check, as the next needs to be gotten to swap the list. + class Factory < Object + attr :type , :for_type , :next_object , :last_object , :reserve , :attribute_name + + PAGE_SIZE = 1024 + RESERVE = 10 + + # initialize for a given type (for_type). The attribute that is used to create the + # list is the first that starts with next_ . "next" itself would have been nice and general + # but is a keyword, so no go. + def initialize(type) + self.for_type = type + self.attribute_name = type.names.find {|name| name.to_s.start_with?("next")} + raise "No next found for #{type.class_name}" unless attribute_name + end + + # get the next free object, advancing the list. + # Calls out to get_more if the list is empty. + # This function is not realy used, as it is hard-coded in risc, but the get_more is + # used, as it get's called from risc (or will) + def get_next_object + unless( next_object ) + self.next_object = reserve + get_more + end + get_head + end + + # this gets the head of the freelist, swaps it out agains the next and returns it + def get_head + nekst = next_object + self.next_object = get_next_for(nekst) + return nekst + end + + # get more from system + # and rebuilt the reserve (get_next already instantiates the reserve) + # + def get_more + chain = get_chain + link = chain + count = RESERVE + while(count > 0) + link = get_next_for(link) + count -= 1 + end + self.next_object = get_next_for(link) + set_next_for( link , nil ) + self.reserve = chain + end + + # this initiates the syscall to get more memory. + # it creates objects from the mem and link them into a chain + def get_chain + raise "type is nil" unless self.for_type + first = sys_mem( for_type , PAGE_SIZE) + chain = first + counter = PAGE_SIZE + while( counter > 0) + nekst = get_next_raw( chain ) + set_next_for(chain, nekst) + chain = nekst + counter -= 1 + end + first + end + + # get the next_* attribute from the given object. + # the attribute name is determined in initialize + def get_next_for(object) + object.send(attribute_name) + end + + # set the next_* attribute of the given object, with the value. + # the attribute name is determined in initialize + def set_next_for(object , value) + object.send("#{attribute_name}=".to_sym , value) + end + + # Return the object _after the given one. In memory terms the next object starts + # after the object ends. So this is in fact pointer arithmetic (once done) + # This implementation will be moved to the adapter, as the real thing needs to be coded + # in risc + # This adapter version just return a new object + def get_next_raw( object ) + sys_mem( object.get_type , 1) + end + + # return more memory from the system. + # Or to be more precise (as that is not really possible), allocate memory + # for PAGE_SIZE objects, and return the first object. + # ( the object has a type as first member, that type will be the for_type of this factory) + # This implementation will be moved to the adapter, as the real thing needs to be coded + # in risc + # This adapter version just return a new object (by establishing the ruby class + # and using ruby's allocate and set_type) + def sys_mem( type , amount) + r_class = eval( "Parfait::#{type.object_class.name}" ) + obj = r_class.allocate + obj.set_type(type) + obj + end + end +end diff --git a/lib/parfait/page.rb b/lib/parfait/page.rb deleted file mode 100644 index aedda2c8..00000000 --- a/lib/parfait/page.rb +++ /dev/null @@ -1,15 +0,0 @@ - -# A Page (from the traditional memory page) represents a collection of -# objects in a physically form. Ie the page holds the memory or data, that -# the objects are made up of. - -# Pages have a total size, but more importantly an object size. -# All objects of a Page are same sized, and multiples of the smallest -# object. The smallest object is usually a cache line, 16 bytes or -# an exponent of two larger. - -module Parfait - class Page < Object - attr :type - end -end diff --git a/lib/risc/parfait_boot.rb b/lib/risc/parfait_boot.rb index d51c5a39..8d420199 100644 --- a/lib/risc/parfait_boot.rb +++ b/lib/risc/parfait_boot.rb @@ -160,6 +160,8 @@ module Parfait NamedList: {}, NilClass: {}, Object: {}, + Factory: { for_type: :Type , next_object: :Object , last_object: :Object , + reserve: :Object , attribute_name: :Word }, ReturnAddress: {next_integer: :ReturnAddress}, Space: {classes: :Dictionary , types: :Dictionary , next_message: :Message , messages: :Message , diff --git a/test/parfait/test_factory.rb b/test/parfait/test_factory.rb new file mode 100644 index 00000000..9be959e3 --- /dev/null +++ b/test/parfait/test_factory.rb @@ -0,0 +1,47 @@ +require_relative "helper" + +module Parfait + class TestPage < ParfaitTest + + def setup + super + @factory = Factory.new Parfait.object_space.get_type_by_class_name(:Integer) + end + def test_ok + assert @factory + end + def test_name_ok + assert @factory.attribute_name.to_s.start_with?("next") + end + def test_get_next + assert_nil @factory.get_next_for( Integer.new(1)) + end + def test_no_next + assert_nil @factory.next_object + assert_nil @factory.last_object + assert_nil @factory.reserve + end + def test_get_next_object + assert_equal Parfait::Integer , @factory.get_next_object.class + end + def test_chain_length + count = 0 + start = @factory.get_next_object + while( start ) + start = start.next_integer + count += 1 + end + assert_equal 1024 - 10 , count + end + def test_reserve_length + count = 0 + start = @factory.get_next_object + start = @factory.reserve + while( start ) + start = start.next_integer + count += 1 + end + assert_equal 11 , count + end + end +end diff --git a/test/parfait/test_space.rb b/test/parfait/test_space.rb index 9f47b2fe..aa2a35c1 100644 --- a/test/parfait/test_space.rb +++ b/test/parfait/test_space.rb @@ -5,7 +5,7 @@ module Parfait def classes [:BinaryCode,:Block,:CacheEntry,:Callable,:CallableMethod,:Class, - :DataObject,:Data4,:Data8,:Data16,:Dictionary,:Integer,:FalseClass, + :DataObject,:Data4,:Data8,:Data16,:Dictionary,:Factory, :Integer,:FalseClass, :List,:Message,:NamedList,:NilClass,:Object,:ReturnAddress, :Space,:TrueClass,:Type,:VoolMethod,:Word] end diff --git a/test/risc/interpreter/calling/test_minus.rb b/test/risc/interpreter/calling/test_minus.rb index 9a353896..001b6e24 100644 --- a/test/risc/interpreter/calling/test_minus.rb +++ b/test/risc/interpreter/calling/test_minus.rb @@ -45,7 +45,7 @@ module Risc ret = main_ticks(64) assert_equal FunctionReturn , ret.class assert_equal :r1 , ret.register.symbol - assert_equal 26472 , @interpreter.get_register(ret.register) + assert_equal 27080 , @interpreter.get_register(ret.register) end end end diff --git a/test/risc/test_interpreter.rb b/test/risc/test_interpreter.rb index 1538ff6a..6fee456e 100644 --- a/test/risc/test_interpreter.rb +++ b/test/risc/test_interpreter.rb @@ -54,7 +54,7 @@ module Risc end def test_pc1 @interpreter.tick - assert_equal 26136 , @interpreter.pc + assert_equal 26680 , @interpreter.pc end def test_tick2 @interpreter.tick @@ -68,7 +68,7 @@ module Risc def test_pc2 @interpreter.tick @interpreter.tick - assert_equal 26140 , @interpreter.pc + assert_equal 26684 , @interpreter.pc end def test_tick_14_jump 14.times {@interpreter.tick} diff --git a/test/risc/test_linker.rb b/test/risc/test_linker.rb index bd3e7402..b285fd66 100644 --- a/test/risc/test_linker.rb +++ b/test/risc/test_linker.rb @@ -25,7 +25,7 @@ module Risc assert_equal 0 , Position.get(@linker.cpu_init).at end def test_cpu_at - assert_equal "0x74cc" , Position.get(@linker.cpu_init.first).to_s + assert_equal "0x76ec" , Position.get(@linker.cpu_init.first).to_s end def test_cpu_label assert_equal Position , Position.get(@linker.cpu_init.first).class