150 lines
3.6 KiB
Ruby
150 lines
3.6 KiB
Ruby
|
require_relative 'parser'
|
||
|
require_relative 'assembler'
|
||
|
require_relative 'objectwriter'
|
||
|
require 'optparse'
|
||
|
require 'ostruct'
|
||
|
|
||
|
module Asm
|
||
|
class CommandLine
|
||
|
def initialize
|
||
|
options = OpenStruct.new
|
||
|
options.output_file = "a.out"
|
||
|
options.target = :arm
|
||
|
|
||
|
opts = OptionParser.new do |opts|
|
||
|
opts.banner = "Usage: as [options] <input file>"
|
||
|
|
||
|
opts.separator ""
|
||
|
opts.separator "Options:"
|
||
|
|
||
|
opts.on("-t", "--target TARGET",
|
||
|
"Specify target architecture (arm [default], ttk91)") { |o|
|
||
|
options.target = o.to_sym
|
||
|
if (not [:arm, :ttk91].include?(options.target))
|
||
|
puts opts
|
||
|
exit
|
||
|
end
|
||
|
}
|
||
|
|
||
|
opts.on("-o", "--output FILENAME",
|
||
|
"Specify output filename for object file") { |o|
|
||
|
options.output_file = o
|
||
|
}
|
||
|
|
||
|
opts.on("-s", "--show-ast",
|
||
|
"Show parse tree") { |o|
|
||
|
options.show_ast = true
|
||
|
}
|
||
|
|
||
|
opts.on_tail("-h", "--help", "Show this message") {
|
||
|
puts opts
|
||
|
exit
|
||
|
}
|
||
|
end
|
||
|
|
||
|
opts.parse!(ARGV)
|
||
|
|
||
|
options.input_file = ARGV.shift
|
||
|
if (not options.input_file)
|
||
|
puts opts
|
||
|
exit
|
||
|
end
|
||
|
|
||
|
@options = options
|
||
|
end
|
||
|
attr_reader :options
|
||
|
|
||
|
def run
|
||
|
begin
|
||
|
if (options.input_file == '-')
|
||
|
code = $stdin.read
|
||
|
else
|
||
|
code = File.read(options.input_file)
|
||
|
end
|
||
|
rescue => err
|
||
|
puts 'as: could not read input file: ' + err.message
|
||
|
exit 2
|
||
|
end
|
||
|
|
||
|
begin
|
||
|
ast = Asm::Parser.parse(code)
|
||
|
rescue Asm::ParseError => err
|
||
|
puts 'as: parse error on line %d, column %d' % [err.line+1, err.column+1]
|
||
|
line = code.split("\n")[err.line]
|
||
|
puts line.gsub(/\s/, ' ')
|
||
|
puts ' ' * (err.column-1) + '^'
|
||
|
puts ' ' + err.message
|
||
|
exit 3
|
||
|
end
|
||
|
|
||
|
if (options.show_ast)
|
||
|
require 'pp'
|
||
|
pp ast
|
||
|
exit 0
|
||
|
end
|
||
|
|
||
|
case options.target
|
||
|
when :arm
|
||
|
require_relative 'arm_assembler.rb'
|
||
|
as_module = Asm::ARM
|
||
|
as_target = Elf::Constants::TARGET_ARM
|
||
|
when :ttk91
|
||
|
require_relative 'ttk91_assembler.rb'
|
||
|
as_module = Asm::TTK91
|
||
|
as_target = Elf::Constants::TARGET_TTK91
|
||
|
end
|
||
|
|
||
|
asm = Asm::AstAssembler.new(as_module)
|
||
|
begin
|
||
|
asm.load_ast ast
|
||
|
data = StringIO.new
|
||
|
asm.assemble(data)
|
||
|
symbols = asm.symbols
|
||
|
rescue Asm::AssemblyError => err
|
||
|
if (err.node)
|
||
|
puts 'as: assembly error on line %d, column %d' % [
|
||
|
err.node.line+1, err.node.column+1]
|
||
|
line = code.split("\n")[err.node.line]
|
||
|
puts line.gsub(/\s/, ' ')
|
||
|
puts ' ' * (err.node.column-1) + '^'
|
||
|
puts ' ' + err.message
|
||
|
else
|
||
|
puts 'as: ' + err.message
|
||
|
end
|
||
|
exit 4
|
||
|
end
|
||
|
|
||
|
writer = Asm::ObjectWriter.new(as_target)
|
||
|
writer.set_text data.string
|
||
|
|
||
|
reloc_name_ref = {}
|
||
|
|
||
|
symbols.each { |symbol|
|
||
|
label = symbol[:label]
|
||
|
if (label.extern?)
|
||
|
reloc_name_ref[label] = symbol[:name]
|
||
|
writer.add_reloc_symbol symbol[:name]
|
||
|
else
|
||
|
writer.add_symbol symbol[:name], symbol[:label].address, symbol[:linkage]
|
||
|
end
|
||
|
}
|
||
|
|
||
|
asm.relocations.each { |reloc|
|
||
|
writer.add_reloc reloc.position, reloc_name_ref[reloc.label], reloc.type
|
||
|
}
|
||
|
|
||
|
begin
|
||
|
writer.save(options.output_file)
|
||
|
rescue => err
|
||
|
puts 'as: cannot save output file: ' + err.message
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
end
|
||
|
|
||
|
|
||
|
if (__FILE__ == $0)
|
||
|
Asm::CommandLine.new.run
|
||
|
end
|