so close i can smell it, checkpoint
This commit is contained in:
@ -16,7 +16,7 @@ module Vm
|
||||
|
||||
# See Value description on how to create code/instructions
|
||||
|
||||
class Block < Value
|
||||
class Block < Code
|
||||
|
||||
def initialize(name)
|
||||
super()
|
||||
@ -31,6 +31,20 @@ module Vm
|
||||
def verify
|
||||
end
|
||||
|
||||
def add_code(kode)
|
||||
kode.at(@position)
|
||||
length = kode.length
|
||||
puts "length #{length}"
|
||||
@position += length
|
||||
@codes << kode
|
||||
end
|
||||
|
||||
def assemble(io)
|
||||
@codes.each do |obj|
|
||||
obj.assemble io
|
||||
end
|
||||
end
|
||||
|
||||
# set the next executed block after self.
|
||||
# why is this useful? if it's unconditional, why not merge them:
|
||||
# So the second block can be used as a jump target. You standard loop needs a block to setup
|
||||
|
@ -1,3 +1,5 @@
|
||||
require_relative "values"
|
||||
|
||||
module Vm
|
||||
# Base class for anything that we can assemble
|
||||
|
||||
@ -9,7 +11,7 @@ module Vm
|
||||
# All code is position independant once assembled.
|
||||
# But for jumps and calls two passes are neccessary.
|
||||
# The first setting the position, the second assembling
|
||||
class Code
|
||||
class Code < Value
|
||||
|
||||
# just sets position to nil, so we can sell that it has not been set
|
||||
def initialize
|
||||
|
@ -5,28 +5,13 @@ module Vm
|
||||
|
||||
#currently just holding the program in here so we can have global access
|
||||
class Context
|
||||
# Make hash attributes to object attributes
|
||||
include Support::HashAttributes
|
||||
|
||||
def initialize program
|
||||
@attributes = {}
|
||||
@attributes["program"] = program
|
||||
end
|
||||
|
||||
# map any function call to an attribute if possible
|
||||
def method_missing name , *args , &block
|
||||
if args.length > 1 or block_given?
|
||||
puts "NO -#{args.length} BLOCK #{block_given?}"
|
||||
super
|
||||
else
|
||||
name = name.to_s
|
||||
if args.length == 1 #must be assignemnt for ir attr= val
|
||||
if name.include? "="
|
||||
return @attributes[name.chop] = args[0]
|
||||
else
|
||||
super
|
||||
end
|
||||
else
|
||||
return @attributes[name]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -1,16 +1,11 @@
|
||||
|
||||
require_relative "code"
|
||||
require "support/hash_attributes"
|
||||
module Vm
|
||||
# Instruction represent the actions that affect change on Values
|
||||
# In an OO way of thinking the Value is data, Instruction the functionality
|
||||
|
||||
# But to allow flexibility, the value api bounces back to the machine api, so machines instantiate
|
||||
# intructions.
|
||||
|
||||
# When Instructions are instantiated the create a linked list of Values and Instructions.
|
||||
# So Value links to Instruction and Instruction links to Value
|
||||
# Also, because the idea of what one instruction does, does not always map one to one to real machine
|
||||
# Because the idea of what one instruction does, does not always map one to one to real machine
|
||||
# instructions, and instruction may link to another instruction thus creating an arbitrary list
|
||||
# to get the job (the original instruciton) done
|
||||
|
||||
# Admittately it would be simpler just to create the (abstract) instructions and let the machine
|
||||
# encode them into what-ever is neccessary, but this approach leaves more possibility to
|
||||
# optimize the actual instruction stream (not just the crystal instruction stream). Makes sense?
|
||||
@ -25,10 +20,14 @@ module Vm
|
||||
# - Call
|
||||
|
||||
# Instruction derives from Code, for the assembly api
|
||||
class Code ; end
|
||||
|
||||
|
||||
class Instruction < Code
|
||||
# Make hash attributes to object attributes
|
||||
include Support::HashAttributes
|
||||
|
||||
def initialize options
|
||||
@options = options
|
||||
end
|
||||
end
|
||||
|
||||
class StackInstruction < Instruction
|
||||
|
@ -11,13 +11,17 @@ module Vm
|
||||
# * Note that register content is typed externally. Not as in mri, where int's are tagged. Floats can's
|
||||
# be tagged and lambda should be it's own type, so tagging does not work
|
||||
|
||||
# Programs are created by invoking methods on subclasses of Value.
|
||||
# But executable code is a sequence of Instructions and subclasses.
|
||||
|
||||
# A Machines main responsibility in the framework is to instantiate Instruction
|
||||
|
||||
# Value functions are mapped to machines by concatenating the values class name + the methd name
|
||||
# Example: SignedValue.plus( value ) -> Machine.signed_plus (value )
|
||||
|
||||
# Also, shortcuts are created to easily instantiate Instruction objects. The "standard" set of instructions
|
||||
# (arm-influenced) provides for normal operations on a register machine,
|
||||
# Example: pop -> StackInstruction.new( {:opcode => :pop}.merge(options) )
|
||||
# Instructions work with options, so you can pass anything in, and the only thing the functions does
|
||||
# is save you typing the clazz.new. It passes the function name as the :opcode
|
||||
|
||||
class Machine
|
||||
|
||||
# hmm, not pretty but for now
|
||||
@ -32,6 +36,51 @@ module Vm
|
||||
# consistency in this code, but also because that is what is actually done
|
||||
attr_reader :status
|
||||
|
||||
|
||||
# here we create the shortcuts for the "standard" instructions, see above
|
||||
# Derived machines may use own instructions and define functions for them if so desired
|
||||
def initialize
|
||||
[:push, :pop].each do |inst|
|
||||
define_instruction(inst , StackInstruction)
|
||||
end
|
||||
|
||||
[:adc, :add, :and, :bic, :eor, :orr, :rsb, :rsc, :sbc, :sub].each do |inst|
|
||||
define_instruction(inst , LogicInstruction)
|
||||
end
|
||||
[:mov, :mvn].each do |inst|
|
||||
define_instruction(inst , MoveInstruction)
|
||||
end
|
||||
[:cmn, :cmp, :teq, :tst].each do |inst|
|
||||
define_instruction(inst , CompareInstruction)
|
||||
end
|
||||
[:strb, :str , :ldrb, :ldr].each do |inst|
|
||||
define_instruction(inst , MemoryInstruction)
|
||||
end
|
||||
[:b, :bl , :swi].each do |inst|
|
||||
define_instruction(inst , CallInstruction)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def create_method(name, &block)
|
||||
self.class.send(:define_method, name , &block)
|
||||
end
|
||||
|
||||
def define_instruction(inst , clazz )
|
||||
c_name = clazz.name
|
||||
my_module = self.class.name.split("::").first
|
||||
clazz_name = clazz.name.split("::").last
|
||||
if(my_module != Vm )
|
||||
module_class = eval("#{my_module}::#{clazz_name}") rescue nil
|
||||
clazz = module_class if module_class
|
||||
end
|
||||
create_method(inst) do |options|
|
||||
options = {} if options == nil
|
||||
options[:opcode] = inst
|
||||
clazz.new(options)
|
||||
end
|
||||
end
|
||||
|
||||
def self.instance
|
||||
@@instance
|
||||
end
|
||||
|
Reference in New Issue
Block a user