ucisc 0.1.3 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile.lock +4 -1
- data/core/stdlib.ucisc +101 -0
- data/examples/everest_12bit.ucisc +195 -0
- data/examples/factorial.ucisc +24 -22
- data/examples/fib.ucisc +40 -35
- data/examples/hello_world.ucisc +59 -68
- data/examples/paint_image.ucisc +261 -0
- data/exe/png_to_hex +38 -0
- data/exe/ucisc +8 -5
- data/lib/micro_cisc.rb +30 -7
- data/lib/micro_cisc/compile/compiler.rb +10 -2
- data/lib/micro_cisc/compile/instruction.rb +40 -13
- data/lib/micro_cisc/compile/statement.rb +118 -34
- data/lib/micro_cisc/version.rb +1 -1
- data/lib/micro_cisc/vm/color_lcd_display.rb +115 -0
- data/lib/micro_cisc/vm/device.rb +32 -21
- data/lib/micro_cisc/vm/processor.rb +20 -9
- data/lib/micro_cisc/vm/term_device.rb +4 -8
- data/ucisc.gemspec +2 -0
- data/ucisc.vim +14 -8
- metadata +36 -4
- data/examples/image.ucisc +0 -543
- data/lib/micro_cisc/vm/video.rb +0 -151
    
        data/exe/ucisc
    CHANGED
    
    | @@ -6,14 +6,17 @@ require "byebug" | |
| 6 6 |  | 
| 7 7 | 
             
            if ARGV.length < 1
         | 
| 8 8 | 
             
              puts "Usage:"
         | 
| 9 | 
            -
              puts "  ucisc <file_name>"
         | 
| 9 | 
            +
              puts "  ucisc <file_name> [<file_name> ...]"
         | 
| 10 10 | 
             
              exit(0)
         | 
| 11 11 | 
             
            end
         | 
| 12 12 |  | 
| 13 | 
            -
             | 
| 14 | 
            -
            puts "Reading #{ | 
| 15 | 
            -
            compiler = MicroCisc.load( | 
| 16 | 
            -
             | 
| 13 | 
            +
            file_names = ARGV.reject { |name| name.start_with?("-") }
         | 
| 14 | 
            +
            puts "Reading #{file_names.join(" ")}"
         | 
| 15 | 
            +
            compiler = MicroCisc.load(file_names)
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            instructions = compiler.command_count
         | 
| 18 | 
            +
            size = compiler.serialize.size
         | 
| 19 | 
            +
            puts "Running program with #{instructions} instructions compiled to #{size} words"
         | 
| 17 20 |  | 
| 18 21 | 
             
            begin
         | 
| 19 22 | 
             
              MicroCisc.run(compiler.serialize)
         | 
    
        data/lib/micro_cisc.rb
    CHANGED
    
    | @@ -13,25 +13,48 @@ require "micro_cisc/vm/device" | |
| 13 13 | 
             
            require "micro_cisc/vm/processor"
         | 
| 14 14 | 
             
            require "micro_cisc/vm/term_device"
         | 
| 15 15 | 
             
            require "micro_cisc/vm/empty_device"
         | 
| 16 | 
            +
            require "micro_cisc/vm/color_lcd_display"
         | 
| 16 17 |  | 
| 17 18 | 
             
            module MicroCisc
         | 
| 18 19 | 
             
              class Error < StandardError; end
         | 
| 19 20 |  | 
| 20 | 
            -
              def self.load( | 
| 21 | 
            -
                text =  | 
| 21 | 
            +
              def self.load(file_names)
         | 
| 22 | 
            +
                text = ""
         | 
| 23 | 
            +
                file_names.each do |file_name|
         | 
| 24 | 
            +
                  text += File.read(file_name)
         | 
| 25 | 
            +
                end
         | 
| 22 26 | 
             
                MicroCisc::Compile::Compiler.new(text)
         | 
| 23 27 | 
             
              end
         | 
| 24 28 |  | 
| 25 29 | 
             
              def self.run(data)
         | 
| 26 | 
            -
                 | 
| 27 | 
            -
                 | 
| 30 | 
            +
                blocks = data.size / 256 + 1
         | 
| 31 | 
            +
                rom_blocks = []
         | 
| 32 | 
            +
                (0...blocks).each do |block|
         | 
| 33 | 
            +
                  rom = Array.new(256).map { 0 }
         | 
| 34 | 
            +
                  size = data.size - block * 256
         | 
| 35 | 
            +
                  size = 256 if size > 256
         | 
| 36 | 
            +
                  rom[0...size] = data[(block * 256)...((block + 1) * 256)]
         | 
| 37 | 
            +
                  rom_blocks << rom
         | 
| 38 | 
            +
                end
         | 
| 28 39 | 
             
                terminal = MicroCisc::Vm::TermDevice.new(5)
         | 
| 29 | 
            -
                 | 
| 30 | 
            -
                 | 
| 40 | 
            +
                screen = MicroCisc::Vm::EmptyDevice.new
         | 
| 41 | 
            +
                init_screen = ARGV.include?('-s')
         | 
| 42 | 
            +
                if(init_screen)
         | 
| 43 | 
            +
                  screen = MicroCisc::Vm::ColorLcdDisplay.new(
         | 
| 44 | 
            +
                    6, # Device ID
         | 
| 45 | 
            +
                    40, # 10k words of memory (40 blocks)
         | 
| 46 | 
            +
                    128, # screen pixel width
         | 
| 47 | 
            +
                    72,  # screen pixel height
         | 
| 48 | 
            +
                    MicroCisc::Vm::ColorLcdDisplay::COLOR_MODE_12BIT
         | 
| 49 | 
            +
                  )
         | 
| 50 | 
            +
                end
         | 
| 51 | 
            +
                devices = Array.new(17).map { MicroCisc::Vm::EmptyDevice.new }
         | 
| 31 52 | 
             
                devices[15] = terminal # first banked device
         | 
| 32 | 
            -
                 | 
| 53 | 
            +
                devices[16] = screen
         | 
| 54 | 
            +
                processor = MicroCisc::Vm::Processor.new(1, 256, rom_blocks)
         | 
| 33 55 | 
             
                processor.devices = devices
         | 
| 34 56 | 
             
                processor.start(ARGV.include?('-d'))
         | 
| 57 | 
            +
                screen.join if(init_screen)
         | 
| 35 58 | 
             
                processor
         | 
| 36 59 | 
             
              end
         | 
| 37 60 |  | 
| @@ -1,8 +1,11 @@ | |
| 1 1 | 
             
            module MicroCisc
         | 
| 2 2 | 
             
              module Compile
         | 
| 3 3 | 
             
                class Compiler
         | 
| 4 | 
            +
                  attr_reader :command_count
         | 
| 5 | 
            +
             | 
| 4 6 | 
             
                  def initialize(text)
         | 
| 5 7 | 
             
                    @text = text
         | 
| 8 | 
            +
                    @command_count = 0
         | 
| 6 9 | 
             
                    parse
         | 
| 7 10 | 
             
                  end
         | 
| 8 11 |  | 
| @@ -11,17 +14,21 @@ module MicroCisc | |
| 11 14 | 
             
                    @instructions = []
         | 
| 12 15 | 
             
                    address = 0
         | 
| 13 16 | 
             
                    @labels = {}
         | 
| 14 | 
            -
                    @ | 
| 17 | 
            +
                    @indexed_vars = {}
         | 
| 18 | 
            +
                    @equivalents = {}
         | 
| 15 19 | 
             
                    errors = []
         | 
| 16 20 | 
             
                    lgen = MicroCisc::Compile::LabelGenerator.new
         | 
| 21 | 
            +
                    @command_count = 0
         | 
| 17 22 | 
             
                    @text.each_line do |line|
         | 
| 18 23 | 
             
                      begin
         | 
| 19 | 
            -
                        statement = MicroCisc::Compile::Statement.new(lgen, line, @ | 
| 24 | 
            +
                        statement = MicroCisc::Compile::Statement.new(lgen, line, @indexed_vars, @equivalents)
         | 
| 25 | 
            +
                        command = false
         | 
| 20 26 | 
             
                        statement.parse.each do |instruction|
         | 
| 21 27 | 
             
                          if instruction.label?
         | 
| 22 28 | 
             
                            @labels[instruction.label] = address
         | 
| 23 29 | 
             
                          elsif instruction.instruction?
         | 
| 24 30 | 
             
                            @instructions << [line_number, instruction]
         | 
| 31 | 
            +
                            command = true
         | 
| 25 32 | 
             
                            address += 1
         | 
| 26 33 | 
             
                          elsif instruction.data?
         | 
| 27 34 | 
             
                            @instructions += instruction.data.map { |d| [line_number, d] }
         | 
| @@ -38,6 +45,7 @@ module MicroCisc | |
| 38 45 | 
             
                            address += word_counts.sum
         | 
| 39 46 | 
             
                          end
         | 
| 40 47 | 
             
                        end
         | 
| 48 | 
            +
                        @command_count += 1 if command
         | 
| 41 49 | 
             
                      rescue ArgumentError => e
         | 
| 42 50 | 
             
                        MicroCisc.logger.error("Error on line #{line_number}: #{e.message}\n  #{line}")
         | 
| 43 51 | 
             
                        errors << [line_number, e, line]
         | 
| @@ -1,19 +1,20 @@ | |
| 1 1 | 
             
            module MicroCisc
         | 
| 2 2 | 
             
              module Compile
         | 
| 3 3 | 
             
                class Instruction
         | 
| 4 | 
            -
                   | 
| 4 | 
            +
                  COMPONENT_WITH_QUOTES_REGEX = /(?<component>[^\s"']+|"((\\"|[^"])*)")/
         | 
| 5 | 
            +
                  attr_reader :label, :operation, :data, :immediates, :sign, :dir, :src, :dest, :original, :minimal, :inc
         | 
| 5 6 |  | 
| 6 | 
            -
                  def initialize(label_generator, minimal, original,  | 
| 7 | 
            +
                  def initialize(label_generator, minimal, original, statement)
         | 
| 7 8 | 
             
                    @label_generator = label_generator
         | 
| 8 9 | 
             
                    @original = original
         | 
| 9 10 | 
             
                    @label = nil
         | 
| 10 11 | 
             
                    @operation = nil
         | 
| 11 12 | 
             
                    @sign = nil
         | 
| 12 13 | 
             
                    @dir = nil
         | 
| 13 | 
            -
                    @ | 
| 14 | 
            +
                    @immediates = []
         | 
| 14 15 | 
             
                    @src = nil
         | 
| 15 16 | 
             
                    @dest = nil
         | 
| 16 | 
            -
                    @ | 
| 17 | 
            +
                    @statement = statement
         | 
| 17 18 | 
             
                    @data = nil
         | 
| 18 19 | 
             
                    @inc = nil
         | 
| 19 20 | 
             
                    @eff = nil
         | 
| @@ -38,7 +39,7 @@ module MicroCisc | |
| 38 39 | 
             
                  end
         | 
| 39 40 |  | 
| 40 41 | 
             
                  def parse_ucisc(minimal_instruction)
         | 
| 41 | 
            -
                    @components = minimal_instruction. | 
| 42 | 
            +
                    @components = minimal_instruction.scan(COMPONENT_WITH_QUOTES_REGEX).flatten
         | 
| 42 43 |  | 
| 43 44 | 
             
                    return if @components.empty?
         | 
| 44 45 |  | 
| @@ -57,6 +58,28 @@ module MicroCisc | |
| 57 58 |  | 
| 58 59 | 
             
                    if @components.first == '%'
         | 
| 59 60 | 
             
                      @components.shift
         | 
| 61 | 
            +
                      @components = @components.map do |component|
         | 
| 62 | 
            +
                        if component =~ /"(\\"|[^"])*"/
         | 
| 63 | 
            +
                          # Remove surrounding quotes
         | 
| 64 | 
            +
                          component = component[1...(component.length - 1)]
         | 
| 65 | 
            +
                          component = component.gsub("\\n","\n")
         | 
| 66 | 
            +
                          component = component.gsub("\\\"","\"")
         | 
| 67 | 
            +
                          hex = []
         | 
| 68 | 
            +
                          offset = 0
         | 
| 69 | 
            +
                          while(offset < component.length)
         | 
| 70 | 
            +
                            pair = component[offset...(offset + 2)]
         | 
| 71 | 
            +
                            pair = pair.bytes
         | 
| 72 | 
            +
                            pair << 0 if pair.length < 2
         | 
| 73 | 
            +
                            word = pair.pack("C*").unpack("S>").last
         | 
| 74 | 
            +
                            hex_word = "%04X" % word
         | 
| 75 | 
            +
                            hex << hex_word
         | 
| 76 | 
            +
                            offset += 2
         | 
| 77 | 
            +
                          end
         | 
| 78 | 
            +
                          component = ["%04X" % component.length] + hex
         | 
| 79 | 
            +
                        else
         | 
| 80 | 
            +
                          component
         | 
| 81 | 
            +
                        end
         | 
| 82 | 
            +
                      end.flatten
         | 
| 60 83 | 
             
                      @data = @components.map do |component|
         | 
| 61 84 | 
             
                        if match = /(?<name>[^\s]+)\.(?<type>imm|disp)/.match(component)
         | 
| 62 85 | 
             
                          [match['name'], match['type']]
         | 
| @@ -98,7 +121,7 @@ module MicroCisc | |
| 98 121 | 
             
                    raise ArgumentError, "Duplicate #{component.last} value" if current
         | 
| 99 122 | 
             
                    code = component.first
         | 
| 100 123 | 
             
                    unless code >= 0 && code < 16
         | 
| 101 | 
            -
                      raise ArgumentError, "Value of #{component.last} must be between 0x0 and 0xF instead of #{component.first.to_s(16).upcase}"
         | 
| 124 | 
            +
                      raise ArgumentError, "Value of #{component.last} must be between 0x0 and 0xF instead of 0x#{component.first.to_s(16).upcase}"
         | 
| 102 125 | 
             
                    end
         | 
| 103 126 | 
             
                    component.first
         | 
| 104 127 | 
             
                  end
         | 
| @@ -135,8 +158,10 @@ module MicroCisc | |
| 135 158 | 
             
                    while components.size > 0
         | 
| 136 159 | 
             
                      to_parse = components.shift
         | 
| 137 160 | 
             
                      if(to_parse.start_with?('$') || to_parse.start_with?('&'))
         | 
| 138 | 
            -
                        raise ArgumentError, "Missing ref #{to_parse}" unless @ | 
| 139 | 
            -
                        to_parse = @ | 
| 161 | 
            +
                        raise ArgumentError, "Missing ref #{to_parse}" unless @statement.get_var(to_parse)
         | 
| 162 | 
            +
                        to_parse = @statement.get_var(to_parse)
         | 
| 163 | 
            +
                        components = to_parse.split(/\s/) + components
         | 
| 164 | 
            +
                        to_parse = components.shift
         | 
| 140 165 | 
             
                      end
         | 
| 141 166 | 
             
                      parsed = parse_component(to_parse)
         | 
| 142 167 | 
             
                      if ['val', 'reg', 'mem'].include?(parsed.last)
         | 
| @@ -198,7 +223,9 @@ module MicroCisc | |
| 198 223 | 
             
                      end
         | 
| 199 224 | 
             
                    end
         | 
| 200 225 |  | 
| 201 | 
            -
                    if @immediates.last != 0 &&  | 
| 226 | 
            +
                    if @immediates.last != 0 && @operation == 'alu'
         | 
| 227 | 
            +
                      raise ArgumentError, "Destination immediate is not allowed for compute instructions"
         | 
| 228 | 
            +
                    elsif @immediates.last != 0 && (!@source_is_mem || !@dest_is_mem)
         | 
| 202 229 | 
             
                      raise ArgumentError, "Destination immediate is only allowed when both arguments are mem args"
         | 
| 203 230 | 
             
                    end
         | 
| 204 231 |  | 
| @@ -208,18 +235,18 @@ module MicroCisc | |
| 208 235 |  | 
| 209 236 | 
             
                  def validate_immediate(value, index)
         | 
| 210 237 | 
             
                    width = @bit_width
         | 
| 211 | 
            -
                    width = width / 2 if @source_is_mem && @dest_is_mem
         | 
| 238 | 
            +
                    width = width / 2 if @operation == 'copy' && @source_is_mem && @dest_is_mem
         | 
| 212 239 | 
             
                    if index == 0 && @source_is_mem || index == 1 && @dest_is_mem
         | 
| 213 240 | 
             
                      min = 0
         | 
| 214 | 
            -
                      max = ( | 
| 241 | 
            +
                      max = (1 << width) - 1
         | 
| 215 242 | 
             
                    else
         | 
| 216 | 
            -
                      magnitude = 1 << ( | 
| 243 | 
            +
                      magnitude = 1 << (width - 1)
         | 
| 217 244 | 
             
                      min = magnitude * -1
         | 
| 218 245 | 
             
                      max = magnitude - 1
         | 
| 219 246 | 
             
                    end
         | 
| 220 247 | 
             
                    if (value < min || value > max)
         | 
| 221 248 | 
             
                      signed = @source_is_mem ? 'unsigned' : 'signed'
         | 
| 222 | 
            -
                      raise ArgumentError, "Immediate max bits is #{ | 
| 249 | 
            +
                      raise ArgumentError, "Immediate max bits is #{width} #{signed}; value must be between #{min} and #{max} instead of #{value}"
         | 
| 223 250 | 
             
                    end
         | 
| 224 251 | 
             
                  end
         | 
| 225 252 |  | 
| @@ -1,16 +1,17 @@ | |
| 1 1 | 
             
            module MicroCisc
         | 
| 2 2 | 
             
              module Compile
         | 
| 3 3 | 
             
                class Statement
         | 
| 4 | 
            -
                  SUGAR_REGEX = /(?< | 
| 4 | 
            +
                  SUGAR_REGEX = /(?<names>(\$|&)[^\s\[\]]+(\s*,\s*(\$|&)[^\s\[\]]+)*)\s+(?<op>as|=)\s+(?<param>.+)/
         | 
| 5 5 | 
             
                  FUNCTION_REGEX = /(?<stack>[^\s\[\]]+)\s*(\[(?<words>[0-9]+)\]){0,1}\s+<=\s+(?<label>[a-zA-Z_][a-zA-Z0-9_\-@$!%]*)\s*\(\s*(?<args>[^)]*)/
         | 
| 6 | 
            -
                  IMM_REGEX = / (0x){0,1} | 
| 6 | 
            +
                  IMM_REGEX = / (?<imm_val>-{0,1}(0x){0,1}[0-9A-Fa-f])\.imm/
         | 
| 7 7 | 
             
                  attr_reader :original, :minimal
         | 
| 8 8 |  | 
| 9 | 
            -
                  def initialize(label_generator, statement,  | 
| 9 | 
            +
                  def initialize(label_generator, statement, indexed_vars, equivalents)
         | 
| 10 10 | 
             
                    @label_generator = label_generator
         | 
| 11 11 | 
             
                    @original = statement
         | 
| 12 12 | 
             
                    @minimal = filter_comments(statement)
         | 
| 13 | 
            -
                    @ | 
| 13 | 
            +
                    @indexed_vars = indexed_vars
         | 
| 14 | 
            +
                    @equivalents = equivalents
         | 
| 14 15 | 
             
                  end
         | 
| 15 16 |  | 
| 16 17 | 
             
                  def filter_comments(instruction)
         | 
| @@ -24,44 +25,124 @@ module MicroCisc | |
| 24 25 | 
             
                    instruction.gsub(/\s+/, ' ')
         | 
| 25 26 | 
             
                  end
         | 
| 26 27 |  | 
| 27 | 
            -
                  def  | 
| 28 | 
            -
                     | 
| 28 | 
            +
                  def normalize(arg, delta)
         | 
| 29 | 
            +
                    imm_matches = arg.scan(IMM_REGEX).flatten
         | 
| 30 | 
            +
                    imm_val = imm_matches.map(&:to_i).sum + delta
         | 
| 31 | 
            +
                    arg = arg.gsub(IMM_REGEX, '').strip
         | 
| 32 | 
            +
                    [arg, imm_val]
         | 
| 33 | 
            +
                  end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                  def create_variable(name, arg, delta)
         | 
| 36 | 
            +
                    unless arg.include?("mem") || arg.include?("reg")
         | 
| 37 | 
            +
                      raise ArgumentError, "Indexed variable reference is not allowed for non-register arguments, use 'as' instead"
         | 
| 38 | 
            +
                    end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                    arg = normalize(arg, delta)
         | 
| 41 | 
            +
                    imm_val = arg.last
         | 
| 42 | 
            +
                    # remove mem/reg part
         | 
| 43 | 
            +
                    arg_num = arg.first[0..-4]
         | 
| 44 | 
            +
                    name = name[1..-1]
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                    mem_name = "$#{name}"
         | 
| 47 | 
            +
                    ref_name = "&#{name}"
         | 
| 48 | 
            +
                    @equivalents.delete(mem_name)
         | 
| 49 | 
            +
                    @equivalents.delete(ref_name)
         | 
| 50 | 
            +
                    @indexed_vars[mem_name] = ["#{arg_num}mem", imm_val]
         | 
| 51 | 
            +
                    @indexed_vars[ref_name] = ["#{arg_num}reg", imm_val]
         | 
| 52 | 
            +
                  end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                  def create_equivalent(name, arg, delta)
         | 
| 55 | 
            +
                    arg = normalize(arg, delta)
         | 
| 56 | 
            +
                    if arg.first.include?("mem") || arg.first.include?("reg")
         | 
| 57 | 
            +
                      imm_val = arg.last
         | 
| 58 | 
            +
                      # remove mem/reg part
         | 
| 59 | 
            +
                      arg_num = arg[0][0..-4]
         | 
| 29 60 | 
             
                      name = name[1..-1]
         | 
| 30 | 
            -
                      arg = arg[0..-4]
         | 
| 31 61 |  | 
| 32 | 
            -
                       | 
| 33 | 
            -
                       | 
| 62 | 
            +
                      mem_name = "$#{name}"
         | 
| 63 | 
            +
                      ref_name = "&#{name}"
         | 
| 64 | 
            +
                      @indexed_vars.delete(mem_name)
         | 
| 65 | 
            +
                      @indexed_vars.delete(ref_name)
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                      imm_str = " #{imm_val}.imm" if imm_val > 0
         | 
| 68 | 
            +
                      @equivalents[mem_name] = "#{arg_num}mem#{imm_str}"
         | 
| 69 | 
            +
                      @equivalents[ref_name] = "#{arg_num}reg#{imm_str}"
         | 
| 34 70 | 
             
                    else
         | 
| 35 | 
            -
                      @ | 
| 71 | 
            +
                      @indexed_vars.delete(name)
         | 
| 72 | 
            +
                      @equivalents[name] = arg.first
         | 
| 36 73 | 
             
                    end
         | 
| 37 74 | 
             
                  end
         | 
| 38 75 |  | 
| 76 | 
            +
                  def update_variable(arg, delta)
         | 
| 77 | 
            +
                    arg = arg.sub('reg', 'mem')
         | 
| 78 | 
            +
                    ['mem', 'reg'].each do |type|
         | 
| 79 | 
            +
                      variable = arg.sub('mem', type)
         | 
| 80 | 
            +
                      @indexed_vars.each do |name, value|
         | 
| 81 | 
            +
                        value[1] += delta if value.first == variable
         | 
| 82 | 
            +
                      end
         | 
| 83 | 
            +
                    end
         | 
| 84 | 
            +
                  end
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                  def get_var(name)
         | 
| 87 | 
            +
                    val = @equivalents[name]
         | 
| 88 | 
            +
                    val ||=
         | 
| 89 | 
            +
                      begin
         | 
| 90 | 
            +
                        pair = @indexed_vars[name]
         | 
| 91 | 
            +
                        "#{pair.first} #{pair.last}.imm" if pair
         | 
| 92 | 
            +
                      end
         | 
| 93 | 
            +
                    val || name
         | 
| 94 | 
            +
                  end
         | 
| 95 | 
            +
             | 
| 39 96 | 
             
                  def parse
         | 
| 97 | 
            +
                    instruction = nil
         | 
| 98 | 
            +
                    names = []
         | 
| 99 | 
            +
                    op = nil
         | 
| 40 100 | 
             
                    if FUNCTION_REGEX =~ @minimal
         | 
| 41 | 
            -
                      parse_function_call
         | 
| 101 | 
            +
                      return parse_function_call
         | 
| 42 102 | 
             
                    elsif SUGAR_REGEX =~ @minimal
         | 
| 43 103 | 
             
                      match = SUGAR_REGEX.match(@minimal)
         | 
| 44 | 
            -
                       | 
| 45 | 
            -
                       | 
| 46 | 
            -
             | 
| 47 | 
            -
             | 
| 48 | 
            -
                       | 
| 49 | 
            -
                        @minimal  | 
| 50 | 
            -
                        instruction = Instruction.new(@label_generator, minimal, original, @sugar)
         | 
| 51 | 
            -
                        dest = instruction.dest
         | 
| 52 | 
            -
                        if [1, 2, 3].include?(dest)
         | 
| 53 | 
            -
                          create_variable(name, "#{dest}.mem")
         | 
| 54 | 
            -
                        else
         | 
| 55 | 
            -
                          dest -= 4 if dest > 4
         | 
| 56 | 
            -
                          create_variable(name, "#{dest}.reg")
         | 
| 57 | 
            -
                        end
         | 
| 58 | 
            -
                        [instruction]
         | 
| 104 | 
            +
                      op = match['op']
         | 
| 105 | 
            +
                      names = match['names'].split(',').map(&:strip)
         | 
| 106 | 
            +
             | 
| 107 | 
            +
                      @minimal = match['param']
         | 
| 108 | 
            +
                      if minimal.start_with?('copy') || minimal.start_with?('compute')
         | 
| 109 | 
            +
                        instruction = Instruction.new(@label_generator, @minimal, original, self)
         | 
| 59 110 | 
             
                      else
         | 
| 60 | 
            -
                         | 
| 111 | 
            +
                        var = match['param']
         | 
| 112 | 
            +
                        imm_match = IMM_REGEX.match(var)
         | 
| 113 | 
            +
                        imm_val = imm_match ? imm_match['imm_val'].to_i : 0
         | 
| 114 | 
            +
                        var = var.to_s.gsub(IMM_REGEX, '').strip
         | 
| 115 | 
            +
                        @minimal = "#{get_var(var)}#{" #{imm_val}.imm" if imm_val != 0}"
         | 
| 61 116 | 
             
                      end
         | 
| 62 117 | 
             
                    else
         | 
| 63 | 
            -
                       | 
| 118 | 
            +
                      instruction = Instruction.new(@label_generator, @minimal, original, self)
         | 
| 64 119 | 
             
                    end
         | 
| 120 | 
            +
                    if instruction && instruction.instruction?
         | 
| 121 | 
            +
                      dest = instruction.dest
         | 
| 122 | 
            +
                      if instruction.inc && instruction.inc > 0
         | 
| 123 | 
            +
                        if dest > 4
         | 
| 124 | 
            +
                          # pop
         | 
| 125 | 
            +
                          update_variable("#{dest - 4}.mem", -1)
         | 
| 126 | 
            +
                        else
         | 
| 127 | 
            +
                          # push
         | 
| 128 | 
            +
                          update_variable("#{dest}.mem", 1)
         | 
| 129 | 
            +
                        end
         | 
| 130 | 
            +
                      end
         | 
| 131 | 
            +
                      if minimal.start_with?('copy') && dest > 4 && instruction.src == dest
         | 
| 132 | 
            +
                        # Manually modifying a register
         | 
| 133 | 
            +
                        update_variable("#{dest - 4}.mem", -1 * instruction.immediates.first)
         | 
| 134 | 
            +
                      end
         | 
| 135 | 
            +
                      dest -= 4 if dest > 4
         | 
| 136 | 
            +
                      @minimal = "#{dest}.mem"
         | 
| 137 | 
            +
                    end
         | 
| 138 | 
            +
                    names.each_with_index do |name, index|
         | 
| 139 | 
            +
                      if op == 'as'
         | 
| 140 | 
            +
                        create_equivalent(name, @minimal, index)
         | 
| 141 | 
            +
                      elsif op == '='
         | 
| 142 | 
            +
                        create_variable(name, @minimal, index)
         | 
| 143 | 
            +
                      end
         | 
| 144 | 
            +
                    end
         | 
| 145 | 
            +
                    [instruction].compact
         | 
| 65 146 | 
             
                  end
         | 
| 66 147 |  | 
| 67 148 | 
             
                  def parse_function_call
         | 
| @@ -69,7 +150,7 @@ module MicroCisc | |
| 69 150 | 
             
                    label = match['label']
         | 
| 70 151 |  | 
| 71 152 | 
             
                    stack = match['stack']
         | 
| 72 | 
            -
                    stack =  | 
| 153 | 
            +
                    stack = get_var(stack)
         | 
| 73 154 | 
             
                    raise ArgumentError, "Invalid stack param, mem register expected: #{stack}" unless stack =~ /^[1-3]\.mem$/
         | 
| 74 155 | 
             
                    stackp = stack.sub('mem', 'reg')
         | 
| 75 156 |  | 
| @@ -79,15 +160,15 @@ module MicroCisc | |
| 79 160 | 
             
                    instructions = []
         | 
| 80 161 | 
             
                    if return_words > 0
         | 
| 81 162 | 
             
                      instruction = "copy #{stackp} -#{return_words}.imm #{stackp}"
         | 
| 82 | 
            -
                      instructions << Instruction.new(@label_generator, instruction, "  #{instruction} # return vars - #{original}",  | 
| 163 | 
            +
                      instructions << Instruction.new(@label_generator, instruction, "  #{instruction} # return vars - #{original}", self)
         | 
| 83 164 | 
             
                    end
         | 
| 84 165 |  | 
| 85 166 | 
             
                    instruction = "copy 0.reg #{args.size + 2}.imm #{stack} push"
         | 
| 86 | 
            -
                    instructions << Instruction.new(@label_generator, instruction, "  #{instruction} # return addr - #{original}",  | 
| 167 | 
            +
                    instructions << Instruction.new(@label_generator, instruction, "  #{instruction} # return addr - #{original}", self)
         | 
| 87 168 |  | 
| 88 169 | 
             
                    stack_delta = 1 + return_words
         | 
| 89 170 | 
             
                    args = args.each do |arg|
         | 
| 90 | 
            -
                      arg = arg.split(' ').map { |a|  | 
| 171 | 
            +
                      arg = arg.split(' ').map { |a| get_var(a) || a }.join(' ')
         | 
| 91 172 | 
             
                      is_stack = arg.start_with?(stack)
         | 
| 92 173 | 
             
                      if is_stack
         | 
| 93 174 | 
             
                        offset = stack_delta
         | 
| @@ -102,10 +183,13 @@ module MicroCisc | |
| 102 183 | 
             
                      end
         | 
| 103 184 | 
             
                      instruction = "copy #{arg} #{stack} push"
         | 
| 104 185 | 
             
                      stack_delta += 1
         | 
| 105 | 
            -
                      instructions << Instruction.new(@label_generator, instruction, "  #{instruction} # push arg - #{original}",  | 
| 186 | 
            +
                      instructions << Instruction.new(@label_generator, instruction, "  #{instruction} # push arg - #{original}", self)
         | 
| 106 187 | 
             
                    end
         | 
| 107 188 | 
             
                    instruction = "copy 0.reg #{label}.disp 0.reg"
         | 
| 108 | 
            -
                    instructions << Instruction.new(@label_generator, instruction, "  #{instruction} # call - #{original}",  | 
| 189 | 
            +
                    instructions << Instruction.new(@label_generator, instruction, "  #{instruction} # call - #{original}", self)
         | 
| 190 | 
            +
                    if return_words > 0
         | 
| 191 | 
            +
                      update_variable(stack, return_words)
         | 
| 192 | 
            +
                    end
         | 
| 109 193 | 
             
                    instructions
         | 
| 110 194 | 
             
                  end
         | 
| 111 195 | 
             
                end
         |