rename register to risc
seems to fit the layer much better as we really have a very reduced instruction set
This commit is contained in:
67
lib/risc/instructions/branch.rb
Normal file
67
lib/risc/instructions/branch.rb
Normal file
@ -0,0 +1,67 @@
|
||||
module Risc
|
||||
|
||||
|
||||
# a branch must branch to a block.
|
||||
class Branch < Instruction
|
||||
def initialize source , to
|
||||
super(source)
|
||||
@label = to
|
||||
end
|
||||
attr_reader :label
|
||||
|
||||
def to_s
|
||||
"#{self.class.name.split("::").last}: #{label ? label.name : ''}"
|
||||
end
|
||||
alias :inspect :to_s
|
||||
|
||||
def length labels = []
|
||||
ret = super(labels)
|
||||
ret += self.label.length(labels) if self.label
|
||||
ret
|
||||
end
|
||||
|
||||
def to_arr( labels = [] )
|
||||
ret = super(labels)
|
||||
ret += self.label.to_arr(labels) if self.label
|
||||
ret
|
||||
end
|
||||
|
||||
def total_byte_length labels = []
|
||||
ret = super(labels)
|
||||
ret += self.label.total_byte_length(labels) if self.label
|
||||
#puts "#{self.class.name} return #{ret}"
|
||||
ret
|
||||
end
|
||||
|
||||
# labels have the same position as their next
|
||||
def set_position position , labels = []
|
||||
set_position self.label.set_position( position , labels ) if self.label
|
||||
super(position,labels)
|
||||
end
|
||||
|
||||
def assemble_all io , labels = []
|
||||
self.assemble(io)
|
||||
self.label.assemble_all(io,labels) if self.label
|
||||
self.next.assemble_all(io, labels) if self.next
|
||||
end
|
||||
|
||||
def each_label labels =[] , &block
|
||||
super
|
||||
self.label.each_label(labels , &block) if self.label
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class IsZero < Branch
|
||||
end
|
||||
|
||||
class IsNotzero < Branch
|
||||
end
|
||||
|
||||
class IsMinus < Branch
|
||||
end
|
||||
|
||||
class IsPlus < Branch
|
||||
end
|
||||
|
||||
end
|
20
lib/risc/instructions/byte_to_reg.rb
Normal file
20
lib/risc/instructions/byte_to_reg.rb
Normal file
@ -0,0 +1,20 @@
|
||||
module Risc
|
||||
|
||||
# ByteToReg moves a single byte into a register from memory.
|
||||
|
||||
# indexes are 1 based (as for slots) , which means we sacrifice a byte of every word
|
||||
# for our sanity
|
||||
|
||||
class ByteToReg < Getter
|
||||
|
||||
end
|
||||
|
||||
# Produce a ByteToReg instruction.
|
||||
# from and to are translated (from symbol to register if neccessary)
|
||||
# but index is left as is.
|
||||
def self.byte_to_reg( source , array , index , to)
|
||||
array = resolve_to_register( array)
|
||||
to = resolve_to_register( to)
|
||||
ByteToReg.new( source , array , index , to)
|
||||
end
|
||||
end
|
33
lib/risc/instructions/function_call.rb
Normal file
33
lib/risc/instructions/function_call.rb
Normal file
@ -0,0 +1,33 @@
|
||||
module Risc
|
||||
# name says it all really
|
||||
# only arg is the method object we want to call
|
||||
# assembly takes care of the rest (ie getting the address)
|
||||
|
||||
class FunctionCall < Instruction
|
||||
def initialize( source , method )
|
||||
super(source)
|
||||
@method = method
|
||||
end
|
||||
attr_reader :method
|
||||
|
||||
def to_s
|
||||
"FunctionCall: #{method.name}"
|
||||
end
|
||||
end
|
||||
|
||||
def self.function_call( source , method )
|
||||
Risc::FunctionCall.new( source , method )
|
||||
end
|
||||
|
||||
def self.issue_call( compiler , callee )
|
||||
return_label = Risc.label("_return_label #{callee.name}" , "#{compiler.type.object_class.name}.#{compiler.method.name}" )
|
||||
ret_tmp = compiler.use_reg(:Label)
|
||||
compiler.add_load_constant("#{callee.name} load ret", return_label , ret_tmp)
|
||||
compiler.add_reg_to_slot("#{callee.name} store ret", ret_tmp , :new_message , :return_address)
|
||||
compiler.add_transfer("#{callee.name} move new message", Risc.new_message_reg , Risc.message_reg )
|
||||
compiler.add_code Risc.function_call( "#{callee.name} call" , callee )
|
||||
compiler.add_code return_label
|
||||
compiler.add_transfer("#{callee.name} remove new message", Risc.message_reg , Risc.new_message_reg )
|
||||
compiler.add_slot_to_reg("#{callee.name} restore message" , :new_message , :caller , :message )
|
||||
end
|
||||
end
|
22
lib/risc/instructions/function_return.rb
Normal file
22
lib/risc/instructions/function_return.rb
Normal file
@ -0,0 +1,22 @@
|
||||
module Risc
|
||||
|
||||
# return from a function call
|
||||
# register and index specify where the return address is stored
|
||||
|
||||
class FunctionReturn < Instruction
|
||||
def initialize( source , register , index)
|
||||
super(source)
|
||||
@register = register
|
||||
@index = index
|
||||
end
|
||||
attr_reader :register , :index
|
||||
|
||||
def to_s
|
||||
"FunctionReturn: #{register} [#{index}]"
|
||||
end
|
||||
end
|
||||
|
||||
def self.function_return( source , register , index)
|
||||
FunctionReturn.new( source , register , index)
|
||||
end
|
||||
end
|
38
lib/risc/instructions/getter.rb
Normal file
38
lib/risc/instructions/getter.rb
Normal file
@ -0,0 +1,38 @@
|
||||
module Risc
|
||||
|
||||
# Getter is a base class for get instructions (SlotToReg and ByteToReg , possibly more coming)
|
||||
#
|
||||
# The instruction that is modelled is loading data from an array into a register
|
||||
#
|
||||
# Getter has a
|
||||
# - an array where the data comes from
|
||||
# - and (array) index
|
||||
# - Risc that the data is moved to
|
||||
|
||||
# Getter and Setter api follow the pattern from -> to
|
||||
|
||||
class Getter < Instruction
|
||||
|
||||
# If you had a c array and index offset
|
||||
# the instruction would do register = array[index]
|
||||
# The arguments are in the order that makes sense for the Instruction name
|
||||
# So SlotToReg means the slot (array and index) moves to the register (last argument)
|
||||
def initialize source , array , index , register
|
||||
super(source)
|
||||
@array = array
|
||||
@index = index
|
||||
@register = register
|
||||
raise "index 0 " if index == 0
|
||||
raise "Not integer or reg #{index}" unless index.is_a?(Numeric) or RiscValue.look_like_reg(index)
|
||||
raise "Not register #{register}" unless RiscValue.look_like_reg(register)
|
||||
raise "Not register #{array}" unless RiscValue.look_like_reg(array)
|
||||
end
|
||||
attr_accessor :array , :index , :register
|
||||
|
||||
def to_s
|
||||
"#{self.class.name.split("::").last}: #{array}[#{index}] -> #{register}"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
85
lib/risc/instructions/label.rb
Normal file
85
lib/risc/instructions/label.rb
Normal file
@ -0,0 +1,85 @@
|
||||
module Risc
|
||||
|
||||
# A label is a placeholder for it's next Instruction
|
||||
# It's function is not to turn into code, but to be a valid brnch target
|
||||
#
|
||||
# So branches and Labels are pairs, fan out, fan in
|
||||
#
|
||||
#
|
||||
|
||||
class Label < Instruction
|
||||
def initialize( source , name , nekst = nil)
|
||||
super(source , nekst)
|
||||
@name = name
|
||||
end
|
||||
attr_reader :name
|
||||
|
||||
def to_s
|
||||
"Label: #{@name} (#{self.next.class.name.split("::").last})"
|
||||
end
|
||||
|
||||
def sof_reference_name
|
||||
@name
|
||||
end
|
||||
|
||||
# a method start has a label of the form Class.method , test for that
|
||||
def is_method
|
||||
@name.split(".").length == 2
|
||||
end
|
||||
|
||||
def to_arr labels = []
|
||||
return [] if labels.include?(self)
|
||||
labels << self
|
||||
super
|
||||
end
|
||||
|
||||
def length labels = []
|
||||
return 0 if labels.include?(self)
|
||||
labels << self
|
||||
ret = 1
|
||||
ret += self.next.length(labels) if self.next
|
||||
ret
|
||||
end
|
||||
|
||||
def assemble io
|
||||
end
|
||||
|
||||
def assemble_all io , labels = []
|
||||
return if labels.include?(self)
|
||||
labels << self
|
||||
self.next.assemble_all(io,labels)
|
||||
end
|
||||
|
||||
def total_byte_length labels = []
|
||||
return 0 if labels.include?(self)
|
||||
labels << self
|
||||
ret = self.next.total_byte_length(labels)
|
||||
#puts "#{self.class.name} return #{ret}"
|
||||
ret
|
||||
end
|
||||
|
||||
# labels have the same position as their next
|
||||
def set_position position , labels = []
|
||||
return position if labels.include?(self)
|
||||
labels << self
|
||||
super(position , labels)
|
||||
self.next.set_position(position,labels)
|
||||
end
|
||||
|
||||
# shame we need this, just for logging
|
||||
def byte_length
|
||||
0
|
||||
end
|
||||
|
||||
def each_label labels =[] , &block
|
||||
return if labels.include?(self)
|
||||
labels << self
|
||||
block.yield(self)
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
def self.label( source , name , nekst = nil)
|
||||
Label.new( source , name , nekst = nil)
|
||||
end
|
||||
end
|
36
lib/risc/instructions/load_constant.rb
Normal file
36
lib/risc/instructions/load_constant.rb
Normal file
@ -0,0 +1,36 @@
|
||||
module Risc
|
||||
# load a constant into a register
|
||||
#
|
||||
# first is the actual constant, either immediate register or object reference (from the space)
|
||||
# second argument is the register the constant is loaded into
|
||||
|
||||
class LoadConstant < Instruction
|
||||
def initialize source , constant , register
|
||||
super(source)
|
||||
@register = register
|
||||
@constant = constant
|
||||
end
|
||||
attr_accessor :register , :constant
|
||||
|
||||
def to_s
|
||||
"LoadConstant: #{register} <- #{constant_str}"
|
||||
end
|
||||
|
||||
private
|
||||
def constant_str
|
||||
case @constant
|
||||
when String , Symbol , Fixnum , Integer
|
||||
@constant.to_s
|
||||
else
|
||||
if( @constant.respond_to? :sof_reference_name )
|
||||
constant.sof_reference_name
|
||||
else
|
||||
constant.class.name.to_s
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
def self.load_constant( source , constant , register )
|
||||
LoadConstant.new source , constant , register
|
||||
end
|
||||
end
|
21
lib/risc/instructions/operator_instruction.rb
Normal file
21
lib/risc/instructions/operator_instruction.rb
Normal file
@ -0,0 +1,21 @@
|
||||
module Risc
|
||||
|
||||
class OperatorInstruction < Instruction
|
||||
def initialize source , operator , left , right
|
||||
super(source)
|
||||
@operator = operator
|
||||
@left = left
|
||||
@right = right
|
||||
end
|
||||
attr_reader :operator, :left , :right
|
||||
|
||||
def to_s
|
||||
"OperatorInstruction: #{left} #{operator} #{right}"
|
||||
end
|
||||
|
||||
end
|
||||
def self.op source , operator , left , right
|
||||
OperatorInstruction.new source , operator , left , right
|
||||
end
|
||||
|
||||
end
|
21
lib/risc/instructions/reg_to_byte.rb
Normal file
21
lib/risc/instructions/reg_to_byte.rb
Normal file
@ -0,0 +1,21 @@
|
||||
module Risc
|
||||
|
||||
# RegToByte moves a byte into memory from a register.
|
||||
|
||||
# indexes are 1 based !
|
||||
|
||||
class RegToByte < Setter
|
||||
|
||||
end
|
||||
|
||||
# Produce a RegToByte instruction.
|
||||
# from and to are translated (from symbol to register if neccessary)
|
||||
# but index is left as is.
|
||||
def self.reg_to_byte( source , from , to , index)
|
||||
from = resolve_to_register from
|
||||
index = resolve_to_index( to , index)
|
||||
to = resolve_to_register to
|
||||
RegToByte.new( source, from , to , index)
|
||||
end
|
||||
|
||||
end
|
26
lib/risc/instructions/reg_to_slot.rb
Normal file
26
lib/risc/instructions/reg_to_slot.rb
Normal file
@ -0,0 +1,26 @@
|
||||
module Risc
|
||||
|
||||
# RegToSlot moves data into memory from a register.
|
||||
# SlotToReg moves data into a register from memory.
|
||||
# Both use a base memory (a register)
|
||||
|
||||
# This is because that is what cpu's can do. In programming terms this would be accessing
|
||||
# an element in an array, in the case of RegToSlot setting the register in the array.
|
||||
|
||||
# btw: to move data between registers, use RiscTransfer
|
||||
|
||||
class RegToSlot < Setter
|
||||
|
||||
end
|
||||
|
||||
# Produce a RegToSlot instruction.
|
||||
# From and to are registers or symbols that can be transformed to a register by resolve_to_register
|
||||
# index resolves with resolve_to_index.
|
||||
def self.reg_to_slot source , from , to , index
|
||||
from = resolve_to_register from
|
||||
index = resolve_to_index( to , index)
|
||||
to = resolve_to_register to
|
||||
RegToSlot.new( source, from , to , index)
|
||||
end
|
||||
|
||||
end
|
35
lib/risc/instructions/register_transfer.rb
Normal file
35
lib/risc/instructions/register_transfer.rb
Normal file
@ -0,0 +1,35 @@
|
||||
module Risc
|
||||
|
||||
# transfer the constents of one register to another.
|
||||
# possibly called move in some cpus
|
||||
|
||||
# There are other instructions to move data from / to memory, namely SlotToReg and RegToSlot
|
||||
|
||||
# Get/Set Slot move data around in vm objects, but transfer moves the objects (in the machine)
|
||||
#
|
||||
# Also it is used for moving temorary data
|
||||
#
|
||||
|
||||
class RiscTransfer < Instruction
|
||||
# initialize with from and to registers.
|
||||
# First argument from
|
||||
# second argument to
|
||||
#
|
||||
# Note: this may be reversed from some assembler notations (also arm)
|
||||
def initialize source , from , to
|
||||
super(source)
|
||||
@from = from
|
||||
@to = to
|
||||
raise "Fix me #{from}" unless from.is_a? RiscValue
|
||||
raise "Fix me #{to}" unless to.is_a? RiscValue
|
||||
end
|
||||
attr_reader :from, :to
|
||||
|
||||
def to_s
|
||||
"RiscTransfer: #{from} -> #{to}"
|
||||
end
|
||||
end
|
||||
def self.transfer( source , from , to)
|
||||
RiscTransfer.new( source , from , to)
|
||||
end
|
||||
end
|
36
lib/risc/instructions/setter.rb
Normal file
36
lib/risc/instructions/setter.rb
Normal file
@ -0,0 +1,36 @@
|
||||
module Risc
|
||||
# Setter is a base class for set instructions (RegToSlot and RegToByte , possibly more coming)
|
||||
#
|
||||
# The instruction that is modelled is loading data from a register into an array
|
||||
#
|
||||
# Setter has a
|
||||
# - Risc that the data is comes from
|
||||
# - an array where the data goes
|
||||
# - and (array) index
|
||||
|
||||
# Getter and Setter api follow the pattern from -> to
|
||||
|
||||
class Setter < Instruction
|
||||
|
||||
# If you had a c array and index offset
|
||||
# the instruction would do array[index] = register
|
||||
# So Setter means the register (first argument) moves to the slot (array and index)
|
||||
def initialize source , register , array , index
|
||||
super(source)
|
||||
@register = register
|
||||
@array = array
|
||||
@index = index
|
||||
raise "index 0 " if index == 0
|
||||
raise "Not integer or reg #{index}" unless index.is_a?(Numeric) or RiscValue.look_like_reg(index)
|
||||
raise "Not register #{register}" unless RiscValue.look_like_reg(register)
|
||||
raise "Not register #{array}" unless RiscValue.look_like_reg(array)
|
||||
end
|
||||
attr_accessor :register , :array , :index
|
||||
|
||||
def to_s
|
||||
"#{self.class.name.split("::").last}: #{register} -> #{array}[#{index}]"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
25
lib/risc/instructions/slot_to_reg.rb
Normal file
25
lib/risc/instructions/slot_to_reg.rb
Normal file
@ -0,0 +1,25 @@
|
||||
module Risc
|
||||
|
||||
# SlotToReg moves data into a register from memory.
|
||||
# RegToSlot moves data into memory from a register.
|
||||
# Both use a base memory (a register)
|
||||
|
||||
# This is because that is what cpu's can do. In programming terms this would be accessing
|
||||
# an element in an array, in the case of SlotToReg setting the value in the array.
|
||||
|
||||
# btw: to move data between registers, use RiscTransfer
|
||||
|
||||
class SlotToReg < Getter
|
||||
|
||||
end
|
||||
|
||||
# Produce a SlotToReg instruction.
|
||||
# Array and to are registers or symbols that can be transformed to a register by resolve_to_register
|
||||
# index resolves with resolve_to_index.
|
||||
def self.slot_to_reg source , array , index , to
|
||||
index = resolve_to_index( array , index)
|
||||
array = resolve_to_register( array )
|
||||
to = resolve_to_register( to )
|
||||
SlotToReg.new( source , array , index , to)
|
||||
end
|
||||
end
|
23
lib/risc/instructions/syscall.rb
Normal file
23
lib/risc/instructions/syscall.rb
Normal file
@ -0,0 +1,23 @@
|
||||
module Risc
|
||||
|
||||
# name says it all really
|
||||
# only arg is the method syscall name
|
||||
# how the layer below executes these is really up to it
|
||||
|
||||
# Any function issuing a Syscall should also save the current message
|
||||
# and restore it after the syscall, saving the return value in Return_index
|
||||
|
||||
class Syscall < Instruction
|
||||
|
||||
def initialize source ,name
|
||||
super(source)
|
||||
@name = name
|
||||
end
|
||||
attr_reader :name
|
||||
|
||||
def to_s
|
||||
"Syscall: #{name}"
|
||||
end
|
||||
|
||||
end
|
||||
end
|
Reference in New Issue
Block a user