2014-08-22 16:40:09 +02:00
|
|
|
module Register
|
2014-05-03 14:13:44 +02:00
|
|
|
|
2014-10-03 10:05:17 +02:00
|
|
|
# the register machine has at least 8 registers, named r0-r5 , :lr and :pc (for historical reasons)
|
|
|
|
# we can load and store their contents and
|
|
|
|
# access (get/set) memory at a constant offset from a register
|
2015-05-24 19:00:11 +02:00
|
|
|
# while the vm works with objects, the register machine has registers,
|
2014-10-03 10:05:17 +02:00
|
|
|
# but we keep the names for better understanding, r4/5 are temporary/scratch
|
|
|
|
# there is no direct memory access, only through registers
|
|
|
|
# constants can/must be loaded into registers before use
|
2015-10-23 20:27:36 +02:00
|
|
|
|
|
|
|
# Instructions form a graph.
|
|
|
|
# Linear instructions form a linked list
|
|
|
|
# Branches fan out, Labels collect
|
|
|
|
# Labels are the only valid branch targets
|
2014-10-03 09:25:10 +02:00
|
|
|
class Instruction
|
2015-10-24 16:12:36 +02:00
|
|
|
include Positioned
|
2014-10-03 09:25:10 +02:00
|
|
|
|
2015-10-23 20:27:36 +02:00
|
|
|
def initialize source , nekst = nil
|
2015-10-28 20:38:23 +01:00
|
|
|
@source = source
|
|
|
|
@next = nekst
|
|
|
|
return unless source
|
|
|
|
raise "Source must be string or ast node, not #{source.class}" unless source.is_a?(String) or source.is_a?(AST::Node)
|
2015-07-18 10:21:49 +02:00
|
|
|
end
|
2015-07-27 11:13:39 +02:00
|
|
|
attr_reader :source
|
2015-07-18 10:21:49 +02:00
|
|
|
|
2015-10-23 20:27:36 +02:00
|
|
|
# set the next instruction (also aliased as <<)
|
|
|
|
# throw an error if that is set, use insert for that use case
|
|
|
|
# return the instruction, so chaining works as one wants (not backwards)
|
|
|
|
def set_next nekst
|
|
|
|
raise "Next already set #{@next}" if @next
|
|
|
|
@next = nekst
|
|
|
|
nekst
|
|
|
|
end
|
|
|
|
alias :<< :set_next
|
|
|
|
|
2015-10-24 10:42:36 +02:00
|
|
|
# during translation we replace one by one
|
|
|
|
def replace_next nekst
|
|
|
|
old = @next
|
|
|
|
@next = nekst
|
2015-10-24 16:12:36 +02:00
|
|
|
@next.append old.next if old
|
2015-10-24 10:42:36 +02:00
|
|
|
end
|
|
|
|
|
2015-10-23 20:27:36 +02:00
|
|
|
# get the next instruction (without arg given )
|
|
|
|
# when given an interger, advance along the line that many time and return.
|
|
|
|
def next( amount = 1)
|
2015-10-24 10:42:36 +02:00
|
|
|
(amount == 1) ? @next : @next.next(amount-1)
|
2015-10-23 20:27:36 +02:00
|
|
|
end
|
|
|
|
# set the give instruction as the next, while moving any existing
|
|
|
|
# instruction along to the given ones's next.
|
|
|
|
# ie insert into the linked list that the instructions form
|
|
|
|
def insert instruction
|
|
|
|
instruction.set_next @next
|
|
|
|
@next = instruction
|
|
|
|
end
|
|
|
|
|
2015-10-24 10:42:36 +02:00
|
|
|
# return last set instruction. ie follow the linked list until it stops
|
|
|
|
def last
|
|
|
|
code = self
|
|
|
|
code = code.next while( code.next )
|
|
|
|
return code
|
|
|
|
end
|
|
|
|
|
|
|
|
# set next for the last (see last)
|
|
|
|
# so append the given code to the linked list at the end
|
|
|
|
def append code
|
|
|
|
last.set_next code
|
|
|
|
end
|
|
|
|
|
2015-10-23 20:27:36 +02:00
|
|
|
def length labels = []
|
|
|
|
ret = 1
|
|
|
|
ret += self.next.length( labels ) if self.next
|
|
|
|
ret
|
2014-06-08 00:41:56 +02:00
|
|
|
end
|
2015-10-23 20:27:36 +02:00
|
|
|
|
|
|
|
def to_ac labels = []
|
|
|
|
ret = [self.class]
|
|
|
|
ret += self.next.to_ac(labels) if self.next
|
|
|
|
ret
|
2014-06-08 00:41:56 +02:00
|
|
|
end
|
2015-10-23 20:27:36 +02:00
|
|
|
|
2015-10-24 16:12:36 +02:00
|
|
|
# derived classes must provide a byte_length
|
|
|
|
def byte_length
|
|
|
|
raise "Abstract called on #{self}"
|
|
|
|
end
|
|
|
|
|
2015-10-25 09:54:19 +01:00
|
|
|
def assemble_all io , labels = []
|
|
|
|
self.assemble(io)
|
|
|
|
self.next.assemble_all(io, labels) if self.next
|
|
|
|
end
|
|
|
|
|
|
|
|
def assemble io
|
|
|
|
raise "Abstract called on #{self}"
|
|
|
|
end
|
|
|
|
|
2015-10-24 16:12:36 +02:00
|
|
|
def total_byte_length labels = []
|
|
|
|
ret = self.byte_length
|
|
|
|
ret += self.next.total_byte_length(labels) if self.next
|
2015-10-25 09:54:19 +01:00
|
|
|
#puts "#{self.class.name} return #{ret}"
|
2015-10-24 16:12:36 +02:00
|
|
|
ret
|
|
|
|
end
|
|
|
|
|
|
|
|
def set_position position , labels = []
|
|
|
|
self.position = position
|
|
|
|
position += byte_length
|
|
|
|
if self.next
|
|
|
|
self.next.set_position(position , labels)
|
|
|
|
else
|
|
|
|
position
|
|
|
|
end
|
|
|
|
end
|
2015-10-25 11:03:31 +01:00
|
|
|
|
|
|
|
def each_label labels =[] , &block
|
|
|
|
self.next.each_label(labels , &block) if self.next
|
|
|
|
end
|
|
|
|
|
2014-05-02 07:02:25 +02:00
|
|
|
end
|
2015-05-24 19:00:11 +02:00
|
|
|
|
2014-05-02 07:02:25 +02:00
|
|
|
end
|
2014-10-03 10:05:17 +02:00
|
|
|
|
2014-10-03 10:07:18 +02:00
|
|
|
require_relative "instructions/set_slot"
|
|
|
|
require_relative "instructions/get_slot"
|
2015-11-19 09:09:24 +01:00
|
|
|
require_relative "instructions/set_byte"
|
|
|
|
require_relative "instructions/get_byte"
|
2014-10-03 10:07:18 +02:00
|
|
|
require_relative "instructions/load_constant"
|
2015-06-22 21:48:42 +02:00
|
|
|
require_relative "instructions/syscall"
|
2014-10-04 11:52:47 +02:00
|
|
|
require_relative "instructions/function_call"
|
|
|
|
require_relative "instructions/function_return"
|
|
|
|
require_relative "instructions/register_transfer"
|
2015-10-23 20:27:36 +02:00
|
|
|
require_relative "instructions/label"
|
2015-07-17 12:21:57 +02:00
|
|
|
require_relative "instructions/branch"
|
2015-08-04 21:01:20 +02:00
|
|
|
require_relative "instructions/operator_instruction"
|