getting a _start and _exit, just missing the actual code
This commit is contained in:
parent
7d20b5e2df
commit
22b5117c8b
@ -40,14 +40,15 @@ module Arm
|
|||||||
end
|
end
|
||||||
|
|
||||||
def main_entry
|
def main_entry
|
||||||
mov( :left => :fp , :right => 0 )
|
entry = Vm::Block.new("main_entry")
|
||||||
|
entry.add_code mov( :left => :fp , :right => 0 )
|
||||||
end
|
end
|
||||||
def main_exit
|
def main_exit
|
||||||
syscall(0)
|
entry = Vm::Block.new("main_exit")
|
||||||
|
entry.add_code syscall(0)
|
||||||
end
|
end
|
||||||
def syscall num
|
def syscall num
|
||||||
mov( :left => 7 , :right => num )
|
[mov( :left => :r7 , :right => num ) , swi( :left => 0 )]
|
||||||
swi( :left => 0 )
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
@ -1,5 +1,5 @@
|
|||||||
require 'arm/nodes'
|
require 'arm/nodes'
|
||||||
require 'arm/block'
|
require 'vm/block'
|
||||||
require 'stream_reader'
|
require 'stream_reader'
|
||||||
require 'stringio'
|
require 'stringio'
|
||||||
require "arm/string_literal"
|
require "arm/string_literal"
|
||||||
@ -43,7 +43,7 @@ module Arm
|
|||||||
def add_string str
|
def add_string str
|
||||||
code = @string_table[str]
|
code = @string_table[str]
|
||||||
return code if code
|
return code if code
|
||||||
data = Arm::StringLiteral.new(str)
|
data = Vm::StringLiteral.new(str)
|
||||||
@string_table[str] = data
|
@string_table[str] = data
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1,83 +0,0 @@
|
|||||||
require_relative 'call_instruction'
|
|
||||||
require_relative 'stack_instruction'
|
|
||||||
require_relative 'logic_instruction'
|
|
||||||
require_relative 'memory_instruction'
|
|
||||||
|
|
||||||
module Arm
|
|
||||||
|
|
||||||
class Code ; end
|
|
||||||
|
|
||||||
# A Block is the smalles unit of code, a list of instructions as it were
|
|
||||||
# It is also a point to jump/branch to. An address in the final stream.
|
|
||||||
# To allow for forward branches creation does not fix the position.
|
|
||||||
# Thee position is fixed in one of three ways
|
|
||||||
# - create the block with ruby block, signalling that the instantiation poin is the position
|
|
||||||
# - call block.code with the code or if you wish program.add_block (and add you code with calls)
|
|
||||||
# - the assmebly process will pin it if it wasn't set
|
|
||||||
|
|
||||||
# creating blocks is done by calling the blocks name/label on either a program or a block
|
|
||||||
# (method missing will cathc the call and create the block)
|
|
||||||
# and the easiest way is to go into a ruby block and start writing instructions
|
|
||||||
# Example (backward jump):
|
|
||||||
# program.loop do create a new block with label loop
|
|
||||||
# sub r1 , r1 , 1 count the r1 register down
|
|
||||||
# bne :loop jump back to loop when the counter is not zero
|
|
||||||
# end (initialization and actual code missing off course)
|
|
||||||
|
|
||||||
# Example (forward jump)
|
|
||||||
# else_block = program.else
|
|
||||||
# program.if do
|
|
||||||
# test r1 , 0 test some condition
|
|
||||||
# beq :else_block
|
|
||||||
# mov . . .. .. do whatever the if block does
|
|
||||||
# end
|
|
||||||
# else_block.code do
|
|
||||||
# ldr .... do whatever else does
|
|
||||||
# end
|
|
||||||
|
|
||||||
# Blocks are also used to create instructions, and so Block has functions for every cpu instruction
|
|
||||||
# and to make using the apu function easier, there are functions that create registers as well
|
|
||||||
class Block < Code
|
|
||||||
|
|
||||||
def initialize(name , prog)
|
|
||||||
super()
|
|
||||||
@name = name.to_sym
|
|
||||||
@codes = []
|
|
||||||
@position = 0
|
|
||||||
@program = prog
|
|
||||||
end
|
|
||||||
attr_reader :name
|
|
||||||
|
|
||||||
# length of the codes. In arm it would be the length * 4
|
|
||||||
# (strings are stored globally in the Assembler)
|
|
||||||
def length
|
|
||||||
@codes.inject(0) {| sum , item | sum + item.length}
|
|
||||||
end
|
|
||||||
|
|
||||||
def add_code(kode)
|
|
||||||
kode.at(@position)
|
|
||||||
length = kode.length
|
|
||||||
@position += length
|
|
||||||
@codes << kode
|
|
||||||
end
|
|
||||||
|
|
||||||
def assemble(io)
|
|
||||||
@codes.each do |obj|
|
|
||||||
obj.assemble io
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# this is used to create blocks.
|
|
||||||
# All functions that have no args are interpreted as block names
|
|
||||||
# In fact the block calls are delegated to the program which then instantiates the blocks
|
|
||||||
def method_missing(meth, *args, &block)
|
|
||||||
if args.length == 0
|
|
||||||
@program.send(meth , *args , &block)
|
|
||||||
else
|
|
||||||
super
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
@ -35,6 +35,7 @@ module Arm
|
|||||||
case @opcode
|
case @opcode
|
||||||
when :b, :bl
|
when :b, :bl
|
||||||
arg = @args[0]
|
arg = @args[0]
|
||||||
|
#puts "BLAB #{arg.inspect}"
|
||||||
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 )
|
||||||
end
|
end
|
||||||
|
@ -17,7 +17,7 @@ module Arm
|
|||||||
|
|
||||||
#(stays in subclases, while build is overriden to provide different arguments)
|
#(stays in subclases, while build is overriden to provide different arguments)
|
||||||
def do_build(arg)
|
def do_build(arg)
|
||||||
if arg.is_a?(Arm::StringLiteral)
|
if arg.is_a?(Vm::StringLiteral)
|
||||||
# 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 )
|
||||||
@ -71,6 +71,7 @@ module Arm
|
|||||||
def assemble(io)
|
def assemble(io)
|
||||||
build
|
build
|
||||||
instuction_class = 0b00 # OPC_DATA_PROCESSING
|
instuction_class = 0b00 # OPC_DATA_PROCESSING
|
||||||
|
puts inspect
|
||||||
val = @operand.is_a?(Symbol) ? reg_code(@operand) : @operand
|
val = @operand.is_a?(Symbol) ? reg_code(@operand) : @operand
|
||||||
val |= (reg_code(@rd) << 12)
|
val |= (reg_code(@rd) << 12)
|
||||||
val |= (reg_code(@rn) << 12+4)
|
val |= (reg_code(@rn) << 12+4)
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
require 'parslet'
|
require 'parslet'
|
||||||
|
|
||||||
require "arm/assembler"
|
|
||||||
require "elf/object_writer"
|
require "elf/object_writer"
|
||||||
require 'parser/composed'
|
require 'parser/composed'
|
||||||
require 'parser/transform'
|
require 'parser/transform'
|
||||||
require "vm/context"
|
require "vm/context"
|
||||||
require "vm/machine"
|
require "vm/machine"
|
||||||
|
require "vm/program"
|
||||||
|
require "stream_reader"
|
@ -34,7 +34,11 @@ module Vm
|
|||||||
end
|
end
|
||||||
|
|
||||||
def add_code(kode)
|
def add_code(kode)
|
||||||
|
if( kode.is_a? Array )
|
||||||
|
kode.each { |code| @codes << code }
|
||||||
|
else
|
||||||
@codes << kode
|
@codes << kode
|
||||||
|
end
|
||||||
self
|
self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ module Vm
|
|||||||
|
|
||||||
# set the position to zero, will have to reset later
|
# set the position to zero, will have to reset later
|
||||||
def initialize
|
def initialize
|
||||||
@address = 0
|
@position = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
# the position in the stream. Think of it as an address if you want. The difference is small.
|
# the position in the stream. Think of it as an address if you want. The difference is small.
|
||||||
@ -21,19 +21,19 @@ 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 @address
|
throw "Not set" unless @position
|
||||||
@address
|
@position
|
||||||
end
|
end
|
||||||
|
|
||||||
# The containing class (assembler/function) call this to tell the instruction/data where it is in the
|
# The containing class (assembler/function) call this to tell the instruction/data where it is in the
|
||||||
# stream. During assembly the position is then used to calculate pc relative addresses.
|
# stream. During assembly the position is then used to calculate pc relative addresses.
|
||||||
def link_at address , context
|
def link_at address , context
|
||||||
@address = address
|
@position = address
|
||||||
end
|
end
|
||||||
|
|
||||||
# length for this code in bytes
|
# length for this code in bytes
|
||||||
def length
|
def length
|
||||||
raise "Not implemented #{self}"
|
raise "Not implemented #{inspect}"
|
||||||
end
|
end
|
||||||
|
|
||||||
# so currently the interface passes the io (usually string_io) in for the code to assemble itself.
|
# so currently the interface passes the io (usually string_io) in for the code to assemble itself.
|
||||||
|
@ -24,16 +24,28 @@ module Vm
|
|||||||
@args.length
|
@args.length
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def link_at address , context
|
||||||
|
# function = context.program.get_function(name)
|
||||||
|
# unless function
|
||||||
|
# function = Vm::Kernel.send(name)
|
||||||
|
# context.program.get_or_create_function( name , function , arity )
|
||||||
|
# end
|
||||||
|
|
||||||
|
@entry.link_at address , context
|
||||||
|
address += @entry.length
|
||||||
|
super(address , context)
|
||||||
|
address += @entry.length
|
||||||
|
@exit.link_at(address,context)
|
||||||
|
end
|
||||||
|
|
||||||
def length
|
def length
|
||||||
@entry.length + @exit.length + super
|
@entry.length + @exit.length + super
|
||||||
end
|
end
|
||||||
|
|
||||||
def compiled context
|
def assemble io
|
||||||
function = context.program.get_function(name)
|
@entry.assemble io
|
||||||
unless function
|
super(io)
|
||||||
function = Vm::Kernel.send(name)
|
@exit.assemble(io)
|
||||||
context.program.get_or_create_function( name , function , arity )
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -5,7 +5,7 @@ module Vm
|
|||||||
Machine.instance.main_entry
|
Machine.instance.main_entry
|
||||||
end
|
end
|
||||||
def self.exit
|
def self.exit
|
||||||
# Machine.exit swi 0
|
# Machine.exit mov r7 , 0 + swi 0
|
||||||
Machine.instance.main_exit
|
Machine.instance.main_exit
|
||||||
end
|
end
|
||||||
def self.puts string
|
def self.puts string
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
require "vm/code"
|
require "vm/code"
|
||||||
|
|
||||||
module Arm
|
module Vm
|
||||||
# The name really says it all.
|
# The name really says it all.
|
||||||
# The only interesting thing is storage.
|
# The only interesting thing is storage.
|
||||||
# Currently string are stored "inline" , ie in the code segment.
|
# Currently string are stored "inline" , ie in the code segment.
|
@ -77,3 +77,4 @@ module Vm
|
|||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
require_relative "string_literal"
|
Loading…
Reference in New Issue
Block a user