block dsl facelift
This commit is contained in:
parent
88ed97ac3b
commit
6fafeda66d
@ -9,20 +9,44 @@ module Asm
|
|||||||
|
|
||||||
# A Block is the smalles unit of code, a list of instructions as it were
|
# 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.
|
# 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. Either set or assembling does that.
|
# 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
|
# 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
|
# and to make using the apu function easier, there are functions that create registers as well
|
||||||
class Block < Code
|
class Block < Code
|
||||||
extend Forwardable # forward block call back to program
|
|
||||||
def_delegator :@program, :block
|
|
||||||
|
|
||||||
def initialize(asm)
|
def initialize(name , prog)
|
||||||
super()
|
super()
|
||||||
|
@name = name.to_sym
|
||||||
@codes = []
|
@codes = []
|
||||||
@position = 0
|
@position = 0
|
||||||
@program = asm
|
@program = prog
|
||||||
end
|
end
|
||||||
|
attr_reader :name
|
||||||
|
|
||||||
ArmMachine::REGISTERS.each do |reg , number|
|
ArmMachine::REGISTERS.each do |reg , number|
|
||||||
define_method(reg) { Asm::Register.new(reg , number) }
|
define_method(reg) { Asm::Register.new(reg , number) }
|
||||||
@ -39,6 +63,9 @@ module Asm
|
|||||||
arg_nodes << @program.add_string(arg)
|
arg_nodes << @program.add_string(arg)
|
||||||
elsif (arg.is_a?(Asm::Block))
|
elsif (arg.is_a?(Asm::Block))
|
||||||
arg_nodes << arg
|
arg_nodes << arg
|
||||||
|
elsif (arg.is_a?(Symbol))
|
||||||
|
block = @program.get_block arg
|
||||||
|
arg_nodes << block
|
||||||
else
|
else
|
||||||
raise "Invalid argument #{arg.inspect} for instruction"
|
raise "Invalid argument #{arg.inspect} for instruction"
|
||||||
end
|
end
|
||||||
@ -84,12 +111,11 @@ module Asm
|
|||||||
define_instruction(inst , CallInstruction)
|
define_instruction(inst , CallInstruction)
|
||||||
end
|
end
|
||||||
|
|
||||||
# setting a block fixes it's position in the stream.
|
# codeing a block fixes it's position in the stream.
|
||||||
# For backwards jumps, positions of blocks are known at creation, but for forward off course not.
|
# You must call with a block, which is instance_eval'd and provides the actual code for the block
|
||||||
# So then one can create a block, branch to it and set it later.
|
def code &block
|
||||||
def set!
|
|
||||||
@program.add_block self
|
@program.add_block self
|
||||||
self
|
self.instance_eval block
|
||||||
end
|
end
|
||||||
|
|
||||||
# length of the codes. In arm it would be the length * 4
|
# length of the codes. In arm it would be the length * 4
|
||||||
@ -111,6 +137,17 @@ module Asm
|
|||||||
end
|
end
|
||||||
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
|
||||||
|
|
||||||
end
|
end
|
@ -53,24 +53,6 @@ module Asm
|
|||||||
@blocks.inject(0) {| sum , item | sum + item.length}
|
@blocks.inject(0) {| sum , item | sum + item.length}
|
||||||
end
|
end
|
||||||
|
|
||||||
# call block to create a new (code) block. The simple way is to do this with a block and
|
|
||||||
# use the yielded block to add code, ie something like:
|
|
||||||
# prog.block do |loop|
|
|
||||||
# loop.instance_eval do #this part you can acheive with calls too
|
|
||||||
# mov r0 , 10
|
|
||||||
# subs r0 , 1
|
|
||||||
# bne block
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
# Easy, because it's a backward jump. For forward branches that doesn't work and so you have to
|
|
||||||
# create the block without a ruby block. You can then jumpt to it immediately
|
|
||||||
# But the block is not part of the program (since we don't know where) and so you have to add it later
|
|
||||||
def block
|
|
||||||
block = Block.new(self)
|
|
||||||
yield block.set! if block_given? #yield the block (which set returns)
|
|
||||||
block
|
|
||||||
end
|
|
||||||
|
|
||||||
# This is how you add a forward declared block. This is called automatically when you
|
# This is how you add a forward declared block. This is called automatically when you
|
||||||
# call block with ruby block, but has to be done manually if not
|
# call block with ruby block, but has to be done manually if not
|
||||||
def add_block block
|
def add_block block
|
||||||
@ -78,6 +60,32 @@ module Asm
|
|||||||
@blocks << block
|
@blocks << block
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# return the block of the given name
|
||||||
|
# or raise an exception, as this is meant to be called when the block is available
|
||||||
|
def get_block name
|
||||||
|
block = @blocks.find {|b| b.name == name}
|
||||||
|
raise "No block found for #{name} (in #{blocks.collect{|b|b.name}.join(':')})" unless block
|
||||||
|
block
|
||||||
|
end
|
||||||
|
# this is used to create blocks.
|
||||||
|
# All functions that have no args are interpreted as block names
|
||||||
|
# and if a block is provided, it is evaluated in the (ruby)blocks scope and the block added to the
|
||||||
|
# program immediately.
|
||||||
|
# If no block is provided (forward declaration), you must call code on it later
|
||||||
|
def method_missing(meth, *args, &block)
|
||||||
|
if args.length == 0
|
||||||
|
code = Block.new(meth.to_s , self )
|
||||||
|
if block_given?
|
||||||
|
add_block code
|
||||||
|
code.instance_eval(&block)
|
||||||
|
end
|
||||||
|
return code
|
||||||
|
else
|
||||||
|
super
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def assemble(io)
|
def assemble(io)
|
||||||
|
@ -13,17 +13,13 @@ class TestSmallProg < MiniTest::Test
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_loop
|
def test_loop
|
||||||
@program.block do |main|
|
@program.main do
|
||||||
main.instance_eval do
|
mov r0, 5 #1
|
||||||
mov r0, 5 #1
|
start do
|
||||||
main.block do |start|
|
subs r0, r0, 1 #2
|
||||||
start.instance_eval do
|
bne :start #3
|
||||||
subs r0, r0, 1 #2
|
mov r7, 1 #4
|
||||||
bne start #3
|
swi 0 #5 5 instructions
|
||||||
end
|
|
||||||
mov r7, 1 #4
|
|
||||||
swi 0 #5 5 instructions
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
write( 5 , "loop" )
|
write( 5 , "loop" )
|
||||||
@ -31,16 +27,14 @@ class TestSmallProg < MiniTest::Test
|
|||||||
|
|
||||||
def test_hello
|
def test_hello
|
||||||
hello = "Hello Raisa\n"
|
hello = "Hello Raisa\n"
|
||||||
@program.block do |main|
|
@program.main do
|
||||||
main.instance_eval do
|
mov r7, 4 # 4 == write
|
||||||
mov r7, 4 # 4 == write
|
mov r0 , 1 # stdout
|
||||||
mov r0 , 1 # stdout
|
add r1 , pc , hello # address of "hello Raisa"
|
||||||
add r1 , pc , hello # address of "hello Raisa"
|
mov r2 , hello.length
|
||||||
mov r2 , hello.length
|
swi 0 #software interupt, ie kernel syscall
|
||||||
swi 0 #software interupt, ie kernel syscall
|
mov r7, 1 # 1 == exit
|
||||||
mov r7, 1 # 1 == exit
|
swi 0
|
||||||
swi 0
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
write(7 + hello.length/4 + 1 , 'hello')
|
write(7 + hello.length/4 + 1 , 'hello')
|
||||||
end
|
end
|
||||||
|
Loading…
Reference in New Issue
Block a user