crystal says Hello.

This commit is contained in:
Torsten Ruger 2014-05-06 21:36:28 +03:00
parent fa123e0354
commit 4135c4d2dc
14 changed files with 134 additions and 114 deletions

View File

@ -33,8 +33,16 @@ module Arm
def word_load value , reg def word_load value , reg
mov( :left => reg , :right => value ) mov( :left => reg , :right => value )
end 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 def function_call call
raise "Not FunctionCall #{call.inspect}" unless call.is_a? Vm::FunctionCall raise "Not FunctionCall #{call.inspect}" unless call.is_a? Vm::FunctionCall
call.args.each do | arg |
end
bl( :left => call.function ) bl( :left => call.function )
end end
@ -44,18 +52,28 @@ module Arm
end end
def main_exit def main_exit
entry = Vm::Block.new("main_exit") entry = Vm::Block.new("main_exit")
entry.add_code syscall(0) entry.add_code syscall(1)
end end
def function_entry f_name def function_entry f_name
entry = Vm::Block.new("#{f_name}_entry") entry = Vm::Block.new("#{f_name}_entry")
entry.add_code push( :left => :lr ) # entry.add_code push( :left => :lr )
end end
def function_exit f_name def function_exit f_name
entry = Vm::Block.new("#{f_name}_exit") entry = Vm::Block.new("#{f_name}_exit")
entry.add_code pop( :left => :pc ) entry.add_code mov( :left => :pc , :right => :lr )
end 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 def syscall num
[mov( :left => :r7 , :right => num ) , swi( :left => 0 )] [mov( :left => :r7 , :right => num ) , swi( :left => 0 )]
end end
end end
end end

View File

@ -4,11 +4,6 @@ module Arm
module LogicHelper module LogicHelper
# ADDRESSING MODE 1 # ADDRESSING MODE 1
# Logic ,Maths, Move and compare instructions (last three below) # 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) # arm intrucioons are pretty sensible, and always 4 bytes (thumb not supported)
def length def length
@ -21,6 +16,7 @@ module Arm
# do pc relative addressing with the difference to the instuction # 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) # 8 is for the funny pipeline adjustment (ie oc pointing to fetch and not execute)
arg = Arm::NumLiteral.new( arg.position - self.position - 8 ) arg = Arm::NumLiteral.new( arg.position - self.position - 8 )
@rn = :pc
end end
if( arg.is_a? Fixnum ) #HACK to not have to change the code just now if( arg.is_a? Fixnum ) #HACK to not have to change the code just now
arg = Arm::NumLiteral.new( arg ) arg = Arm::NumLiteral.new( arg )
@ -36,7 +32,7 @@ module Arm
else else
raise "cannot fit numeric literal argument in operand #{arg}" raise "cannot fit numeric literal argument in operand #{arg}"
end end
elsif (arg.is_a?(Arm::Register)) elsif (arg.is_a?(Symbol))
@operand = arg @operand = arg
@i = 0 @i = 0
elsif (arg.is_a?(Arm::Shift)) elsif (arg.is_a?(Arm::Shift))
@ -64,7 +60,7 @@ module Arm
@operand = rm_ref | (shift_op << 4) | (shift_imm << 4+3) @operand = rm_ref | (shift_op << 4) | (shift_imm << 4+3)
else else
raise "invalid operand argument #{arg.inspect}" raise "invalid operand argument #{arg.inspect} , #{inspect}"
end end
end end
@ -87,7 +83,7 @@ module Arm
include LogicHelper include LogicHelper
def initialize(options) def initialize(options)
super(options) super(options)
@update_status_flag = 0 @update_status_flag = 0
@condition_code = :al @condition_code = :al
@opcode = options[:opcode] @opcode = options[:opcode]
@ -96,9 +92,14 @@ module Arm
@rn = nil @rn = nil
@i = 0 @i = 0
@rd = args[0] @rd = @args[0]
end end
attr_accessor :i, :rn, :rd attr_accessor :i, :rn, :rd
# Build representation for source value
def build
@rn = @args[1]
do_build @args[2]
end
end end
class CompareInstruction < Vm::CompareInstruction class CompareInstruction < Vm::CompareInstruction

View File

@ -7,16 +7,22 @@ module Arm
class MemoryInstruction < Vm::MemoryInstruction class MemoryInstruction < Vm::MemoryInstruction
include Arm::Constants include Arm::Constants
def initialize(opcode , condition_code , update_status , args) def initialize(options)
super(opcode , condition_code , update_status , args) 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) @i = 0 #I flag (third bit)
@pre_post_index = 0 #P flag @pre_post_index = 0 #P flag
@add_offset = 0 #U flag @add_offset = 0 #U flag
@byte_access = opcode.to_s[-1] == "b" ? 1 : 0 #B (byte) flag @byte_access = opcode.to_s[-1] == "b" ? 1 : 0 #B (byte) flag
@w = 0 #W flag @w = 0 #W flag
@is_load = opcode.to_s[0] == "l" ? 1 : 0 #L (load) flag @is_load = opcode.to_s[0] == "l" ? 1 : 0 #L (load) flag
@rn = reg "r0" # register zero = zero bit pattern @rn = :r0 # register zero = zero bit pattern
@rd = reg "r0" # register zero = zero bit pattern @rd = :r0 # register zero = zero bit pattern
end end
attr_accessor :i, :pre_post_index, :add_offset, attr_accessor :i, :pre_post_index, :add_offset,
:byte_access, :w, :is_load, :rn, :rd :byte_access, :w, :is_load, :rn, :rd
@ -29,17 +35,17 @@ module Arm
# Build representation for target address # Build representation for target address
def build def build
if( @is_load ) if( @is_load )
@rd = args[0] @rd = @args[0]
arg = args[1] arg = @args[1]
else #store else #store
@rd = args[1] @rd = @args[1]
arg = args[0] arg = @args[0]
end end
#str / ldr are _serious instructions. With BIG possibilities not half are implemented #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 @rn = arg
if(arg.offset != 0) if options[:offset]
@operand = arg.offset @operand = options[:offset]
if (@operand < 0) if (@operand < 0)
@add_offset = 0 @add_offset = 0
#TODO test/check/understand #TODO test/check/understand
@ -51,6 +57,13 @@ module Arm
raise "reference offset too large/small (max 4095) #{arg} #{inspect}" raise "reference offset too large/small (max 4095) #{arg} #{inspect}"
end end
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)) elsif (arg.is_a?(Arm::Label) or arg.is_a?(Arm::NumLiteral))
@pre_post_index = 1 @pre_post_index = 1
@rn = pc @rn = pc
@ -69,9 +82,9 @@ module Arm
@add_offset = 1 @add_offset = 1
@pre_post_index = 1 @pre_post_index = 1
instuction_class = 0b01 # OPC_MEMORY_ACCESS instuction_class = 0b01 # OPC_MEMORY_ACCESS
val = operand val = @operand.is_a?(Symbol) ? reg_code(@operand) : @operand
val |= (rd.bits << 12 ) val |= (reg_code(rd) << 12 )
val |= (rn.bits << 12+4) #16 val |= (reg_code(rn) << 12+4) #16
val |= (is_load << 12+4 +4) val |= (is_load << 12+4 +4)
val |= (w << 12+4 +4+1) val |= (w << 12+4 +4+1)
val |= (byte_access << 12+4 +4+1+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 |= (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) val |= (cond_bit_code << 12+4 +4+1+1+1+1 +1+1+2)
io.write_uint32 val io.write_uint32 val
end end
end end
end end

View File

@ -29,8 +29,9 @@ module Ast
end end
def compile context def compile context
# TODO check if needst to be added? value = Vm::StringLiteral.new(string)
Vm::ObjectReference.new( Vm::StringValue.new(string) ) context.program.add_object value
value
end end
def == other def == other
compare other , [:string] compare other , [:string]

View File

@ -12,8 +12,8 @@ module Ast
fun = Vm::FunctionCall.new( name , args.collect{ |a| a.compile(context) } ) fun = Vm::FunctionCall.new( name , args.collect{ |a| a.compile(context) } )
fun.assign_function context fun.assign_function context
fun.load_args fun.load_args
#puts "funcall #{self.inspect}"
fun.do_call fun.do_call
fun
end end
def == other def == other

View File

@ -2,7 +2,7 @@ module Core
class Kernel class Kernel
#there are no Kernel instances, only class methods. #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 module ClassMethods
def main_start def main_start
#TODO extract args into array of strings #TODO extract args into array of strings
@ -18,9 +18,9 @@ module Core
def function_exit f_name def function_exit f_name
Vm::Machine.instance.function_exit f_name Vm::Machine.instance.function_exit f_name
end end
def self.puts string def putstring
# should unwrap from string to char* # should unwrap from string to char*
Vm::Machine.instance.puts string Vm::Machine.instance.putstring
end end
end end

View File

@ -56,12 +56,12 @@ module Vm
obj.assemble io obj.assemble io
end end
end end
# set the next executed block after self. # set the next executed block after self.
# why is this useful? if it's unconditional, why not merge them: # 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 # 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 # and at least one to do the calculation
def next block def set_next block
@next = block @next = block
end end

View File

@ -21,7 +21,6 @@ module Vm
# in other words, during assembly the position _must_ be resolved into a pc relative address # in other words, during assembly the position _must_ be resolved into a pc relative address
# and not used as is # and not used as is
def position def position
throw "Not set" unless @position
@position @position
end end

View File

@ -6,49 +6,67 @@ module Vm
# Functions also have arguments, though they are handled differently (in register allocation) # 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 # Functions have a exactly three blocks, entry, exit and body, which are created for you
# but there is no branch created between them, this must be done by the programmer. # 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 = []) def initialize(name , args = [])
super(name) super()
@name = name
@args = args @args = args
@entry = Core::Kernel::function_entry( name ) @entry = Core::Kernel::function_entry( name )
@exit = Core::Kernel::function_exit( name ) @exit = Core::Kernel::function_exit( name )
@body = Block.new("#{name}_body")
branch_body
end 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 def arity
@args.length @args.length
end end
def link_at address , context def link_at address , context
# function = context.program.get_function(name) raise "undefined code #{inspect}" if @body.nil?
# unless function super #just sets the position
# function = Core::Kernel.send(name)
# context.program.get_or_create_function( name , function , arity )
# end
@entry.link_at address , context @entry.link_at address , context
address += @entry.length address += @entry.length
super(address , context) @body.link_at(address , context)
address += @entry.length address += @entry.length
@exit.link_at(address,context) @exit.link_at(address,context)
end end
def length def length
@entry.length + @exit.length + super @entry.length + @exit.length + @body.length
end end
def assemble io def assemble io
@entry.assemble io @entry.assemble(io)
super(io) @body.assemble(io)
@exit.assemble(io) @exit.assemble(io)
end 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 def add_arg value
# TODO check # TODO check
@args << value @args << value

View File

@ -21,11 +21,12 @@ module Vm
end end
def load_args def load_args
args.each_with_index do |arg , index| args.each_with_index do |arg , index|
arg.load index add_code arg.load(index)
end end
end end
def do_call def do_call
Machine.instance.function_call self add_code Machine.instance.function_call self
end end
end end
end end

View File

@ -49,6 +49,8 @@ module Vm
fun = get_function name fun = get_function name
unless fun unless fun
fun = Function.new(name) fun = Function.new(name)
block = Core::Kernel.send(name)
fun.set_body block
@functions << fun @functions << fun
end end
fun fun

View File

@ -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

View File

@ -41,40 +41,36 @@ module Vm
end end
end end
class Float < Word # The name really says it all.
end # 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 class StringLiteral < Value
end
class StringValue < Value # currently aligned to 4 (ie padded with 0) and off course 0 at the end
def initialize string def initialize(str)
@string = string super()
end length = str.length
def length # rounding up to the next 4 (always adding one for zero pad)
@string.length + 3 pad = ((length / 4 ) + 1 ) * 4 - length
raise "#{pad} #{self}" unless pad >= 1
@string = str + "\x00" * pad
end end
attr_reader :string attr_reader :string
end
class MemoryReference < Reference
end
class ObjectReference < Reference def load reg_num
def initialize obj Machine.instance.string_load self , reg_num
@object = obj end
# the strings length plus padding
def length
@string.length
end end
attr_reader :object
def compiled context # just writing the string
if object.is_a? StringValue def assemble(io)
context.program.add_object object io << @string
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
end end
end end
end end
require_relative "string_literal"

View File

@ -1 +1 @@
puts( "hello world" ) putstring( "Hello Raisa, I am crystksdfkljsncjncn" )