class SnesUtils::LineAssembler
Public Class Methods
new(raw_line, **options)
click to toggle source
# File lib/vas/vas.rb, line 245 def initialize(raw_line, **options) @line = raw_line.split(';').first.strip.chomp @current_address = (options[:program_counter] + options[:origin]) @cpu = options[:cpu] @label_registry = options[:label_registry] end
Public Instance Methods
assemble()
click to toggle source
# File lib/vas/vas.rb, line 252 def assemble instruction = @line.split(' ') mnemonic = instruction[0].upcase raw_operand = instruction[1].to_s raw_operand = replace_label(raw_operand) opcode_data = detect_opcode(mnemonic, raw_operand) raise "Invalid syntax" unless opcode_data opcode = opcode_data[:opcode] @mode = opcode_data[:mode] @length = opcode_data[:length] operand_data = detect_operand(raw_operand) operand = process_operand(operand_data) return [opcode, *operand] end
bit_instruction?()
click to toggle source
# File lib/vas/vas.rb, line 399 def bit_instruction? SnesUtils.const_get(@cpu.capitalize)::Definitions::BIT_INSTRUCTIONS.include?(@mode) end
contains_label?(operand)
click to toggle source
# File lib/vas/vas.rb, line 273 def contains_label?(operand) Vas::LABEL_OPERATORS.any? { |s| operand.include?(s[-1,1]) } end
detect_opcode(mnemonic, operand)
click to toggle source
# File lib/vas/vas.rb, line 326 def detect_opcode(mnemonic, operand) SnesUtils.const_get(@cpu.capitalize)::Definitions::OPCODES_DATA.detect do |row| mode = row[:mode] regex = SnesUtils.const_get(@cpu.capitalize)::Definitions::MODES_REGEXES[mode] row[:mnemonic] == mnemonic && regex =~ operand end end
detect_operand(raw_operand)
click to toggle source
# File lib/vas/vas.rb, line 334 def detect_operand(raw_operand) SnesUtils.const_get(@cpu.capitalize)::Definitions::MODES_REGEXES[@mode].match(raw_operand) end
double_operand_instruction?()
click to toggle source
# File lib/vas/vas.rb, line 395 def double_operand_instruction? SnesUtils.const_get(@cpu.capitalize)::Definitions::DOUBLE_OPERAND_INSTRUCTIONS.include?(@mode) end
little_endian(operand, length)
click to toggle source
# File lib/vas/vas.rb, line 385 def little_endian(operand, length) if length > 2 [((operand >> 0) & 0xff), ((operand >> 8) & 0xff), ((operand >> 16) & 0xff)] elsif length > 1 [((operand >> 0) & 0xff), ((operand >> 8) & 0xff)] else operand end end
process_bit_operand(operand_data)
click to toggle source
# File lib/vas/vas.rb, line 358 def process_bit_operand(operand_data) m = operand_data[1].to_i(16) raise "Out of range: m > 0x1fff: #{m}" if m > 0x1fff b = operand_data[2].to_i(16) raise "Out of range: b > 7: #{b}" if b > 7 little_endian(m << 3 | b, 2) end
process_double_operand_instruction(operand_data)
click to toggle source
# File lib/vas/vas.rb, line 347 def process_double_operand_instruction(operand_data) if bit_instruction? process_bit_operand(operand_data) else operands = [operand_data[1], operand_data[2]].map { |o| o.to_i(16) } operand_2 = rel_instruction? ? process_rel_operand(operands[1]) : operands[1] rel_instruction? ? [operands[0], operand_2] : [operand_2, operands[0]] end end
process_operand(operand_data)
click to toggle source
# File lib/vas/vas.rb, line 338 def process_operand(operand_data) if double_operand_instruction? process_double_operand_instruction(operand_data) else operand = operand_data[1]&.to_i(16) rel_instruction? ? process_rel_operand(operand) : little_endian(operand, @length - 1) end end
process_rel_operand(operand)
click to toggle source
# File lib/vas/vas.rb, line 368 def process_rel_operand(operand) relative_addr = operand - (@current_address & 0x00ffff) - @length if @cpu == Vas::WDC65816 && @mode == :rell raise "Relative address out of range: #{relative_addr}" if relative_addr < -32_768 || relative_addr > 32_767 relative_addr += 0x10000 if relative_addr < 0 little_endian(relative_addr, 2) else raise "Relative address out of range: #{relative_addr}" if relative_addr < -128 || relative_addr > 127 relative_addr += 0x100 if relative_addr < 0 relative_addr end end
rel_instruction?()
click to toggle source
# File lib/vas/vas.rb, line 403 def rel_instruction? SnesUtils.const_get(@cpu.capitalize)::Definitions::REL_INSTRUCTIONS.include?(@mode) end
replace_label(operand)
click to toggle source
# File lib/vas/vas.rb, line 285 def replace_label(operand) return operand unless contains_label?(operand) unless matches = /(#{Vas::LABEL_OPERATORS.join('|')})(\w+)(\+(\d+))?/.match(operand) raise "Invalid label syntax: #{operand}" end mode = matches[1] label = matches[2] offset = matches[4].to_i label_data = @label_registry.detect { |l| l[0] == label } value = label_data ? label_data[1] : @current_address value += offset case mode when '@' value = value & 0x00ffff new_value = Vas::hex(value, 4) when '!' value = value | (((@current_address >> 16) & 0xff) << 16) new_value = Vas::hex(value, 6) when '<' value = value & 0x0000ff new_value = Vas::hex(value) when '>' value = (value & 0x00ff00) >> 8 new_value = Vas::hex(value) when '^' mode = '\^' value = (value & 0xff0000) >> 16 new_value = Vas::hex(value) else raise "Mode error: #{mode}" end operand.gsub(/(#{mode})\w+(\+(\d+))?/, new_value) end
replace_labels(operand)
click to toggle source
# File lib/vas/vas.rb, line 277 def replace_labels(operand) while contains_label?(operand) operand = replace_label(operand) end operand end