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
|
||||
# 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
|
||||
# and to make using the apu function easier, there are functions that create registers as well
|
||||
class Block < Code
|
||||
extend Forwardable # forward block call back to program
|
||||
def_delegator :@program, :block
|
||||
|
||||
def initialize(asm)
|
||||
def initialize(name , prog)
|
||||
super()
|
||||
@name = name.to_sym
|
||||
@codes = []
|
||||
@position = 0
|
||||
@program = asm
|
||||
@program = prog
|
||||
end
|
||||
attr_reader :name
|
||||
|
||||
ArmMachine::REGISTERS.each do |reg , number|
|
||||
define_method(reg) { Asm::Register.new(reg , number) }
|
||||
@ -39,6 +63,9 @@ module Asm
|
||||
arg_nodes << @program.add_string(arg)
|
||||
elsif (arg.is_a?(Asm::Block))
|
||||
arg_nodes << arg
|
||||
elsif (arg.is_a?(Symbol))
|
||||
block = @program.get_block arg
|
||||
arg_nodes << block
|
||||
else
|
||||
raise "Invalid argument #{arg.inspect} for instruction"
|
||||
end
|
||||
@ -84,12 +111,11 @@ module Asm
|
||||
define_instruction(inst , CallInstruction)
|
||||
end
|
||||
|
||||
# setting 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.
|
||||
# So then one can create a block, branch to it and set it later.
|
||||
def set!
|
||||
# codeing a block fixes it's position in the stream.
|
||||
# You must call with a block, which is instance_eval'd and provides the actual code for the block
|
||||
def code &block
|
||||
@program.add_block self
|
||||
self
|
||||
self.instance_eval block
|
||||
end
|
||||
|
||||
# length of the codes. In arm it would be the length * 4
|
||||
@ -111,6 +137,17 @@ module Asm
|
||||
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
|
@ -53,24 +53,6 @@ module Asm
|
||||
@blocks.inject(0) {| sum , item | sum + item.length}
|
||||
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
|
||||
# call block with ruby block, but has to be done manually if not
|
||||
def add_block block
|
||||
@ -78,6 +60,32 @@ module Asm
|
||||
@blocks << block
|
||||
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
|
||||
|
||||
def assemble(io)
|
||||
|
@ -13,26 +13,21 @@ class TestSmallProg < MiniTest::Test
|
||||
end
|
||||
|
||||
def test_loop
|
||||
@program.block do |main|
|
||||
main.instance_eval do
|
||||
@program.main do
|
||||
mov r0, 5 #1
|
||||
main.block do |start|
|
||||
start.instance_eval do
|
||||
start do
|
||||
subs r0, r0, 1 #2
|
||||
bne start #3
|
||||
end
|
||||
bne :start #3
|
||||
mov r7, 1 #4
|
||||
swi 0 #5 5 instructions
|
||||
end
|
||||
end
|
||||
end
|
||||
write( 5 , "loop" )
|
||||
end
|
||||
|
||||
def test_hello
|
||||
hello = "Hello Raisa\n"
|
||||
@program.block do |main|
|
||||
main.instance_eval do
|
||||
@program.main do
|
||||
mov r7, 4 # 4 == write
|
||||
mov r0 , 1 # stdout
|
||||
add r1 , pc , hello # address of "hello Raisa"
|
||||
@ -41,7 +36,6 @@ class TestSmallProg < MiniTest::Test
|
||||
mov r7, 1 # 1 == exit
|
||||
swi 0
|
||||
end
|
||||
end
|
||||
write(7 + hello.length/4 + 1 , 'hello')
|
||||
end
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user