diff --git a/lib/risc/interpreter.rb b/lib/risc/interpreter.rb index 58e5635c..882260aa 100644 --- a/lib/risc/interpreter.rb +++ b/lib/risc/interpreter.rb @@ -8,7 +8,8 @@ module Risc # will be executed by method execute_SlotToReg # # The Interpreter (a bit like a cpu) has a state flag, a current instruction and registers - # We collect the stdout (as a hack not to interpret the OS) + # We collect the stdout (as a hack not to interpret the OS) in a string. It can also be passed + # in to the init, as an IO # class Interpreter # fire events for changed pc and register contents @@ -18,11 +19,13 @@ module Risc attr_reader :instruction , :clock , :pc # current instruction and pc attr_reader :registers # the registers, 16 (a hash, sym -> contents) - attr_reader :stdout, :state , :flags # somewhat like the lags on a cpu, hash sym => bool (zero .. . ) + attr_reader :stdout, :state , :flags # somewhat like the flags on a cpu, hash sym => bool (zero .. . ) - #start in state :stopped and set registers to unknown - def initialize( linker ) - @stdout , @clock , @pc , @state = "", 0 , 0 , :stopped + # start in state :stopped and set registers to unknown + # Linker gives the state of the program + # Passing a stdout in (an IO, only << called) can be used to get output immediately. + def initialize( linker , stdout = "") + @stdout , @clock , @pc , @state = stdout, 0 , 0 , :stopped @registers = {} @flags = { :zero => false , :plus => false , :minus => false , :overflow => false } @@ -32,8 +35,7 @@ module Risc @linker = linker end - def start_program(linker = nil) - initialize(linker || @linker) + def start_program() init = @linker.cpu_init set_state(:running) set_pc( Position.get(init).at ) @@ -249,10 +251,12 @@ module Risc str = get_register( :r1 ) # should test length, ie r2 case str when Symbol - @stdout += str.to_s + @stdout << str.to_s + @stdout.flush if @stdout.respond_to?(:flush) return str.to_s.length when Parfait::Word - @stdout += str.to_string + @stdout << str.to_string + @stdout.flush if @stdout.respond_to?(:flush) return str.char_length else raise "NO string for putstring #{str.class}:#{str.object_id}" unless str.is_a?(Symbol) diff --git a/lib/rubyx/rubyxc.rb b/lib/rubyx/rubyxc.rb index 3483e11b..93b21a0d 100644 --- a/lib/rubyx/rubyxc.rb +++ b/lib/rubyx/rubyxc.rb @@ -1,5 +1,6 @@ require "thor" require "rubyx" +require "risc/interpreter" class RubyXC < Thor desc "compile FILE" , "Compile given FILE to binary" @@ -27,7 +28,44 @@ class RubyXC < Thor outfile = file.split("/").last.gsub(".rb" , ".o") writer.save outfile + end + + desc "interpret FILE" , "Interpret given FILE " + long_desc <<-LONGDESC + Compiles the given file to an intermediate RISC format, and runs the + Interpreter. + + RISC is the last abstract layer inside the compiler. It is in nature + very close to arm (without quirks and much smaller). + + An interpreter was originally developed for the RISC layer for debugging purposes. + Running the interpreter is about 50k slower than binary, but it can be used + to veryfy simple programs. + + No output file will be generated, the only output is generated by the + given program. + + The program must define a main method on the Space class, which will be invoked. + LONGDESC + + def interpret(file) + begin + ruby = File.read(file) + rescue + fail MalformattedArgumentError , "No such file #{file}" + end + options = { + parfait: { factory: 3*1024, }, + load_parfait: false , + } + compiler = RubyX::RubyXCompiler.new(options) + linker = compiler.ruby_to_binary(ruby, :interpreter) + + puts "interpreting #{file}" + + interpreter = Risc::Interpreter.new(linker , STDOUT ) + interpreter.start_program + interpreter.tick while(interpreter.instruction) - return outfile end end