keep some stuff in storage that just gets in tway in crystal
This commit is contained in:
parent
8de59a85ba
commit
8584c93575
48
unused/addr_table_object.rb
Normal file
48
unused/addr_table_object.rb
Normal file
@ -0,0 +1,48 @@
|
||||
module Asm
|
||||
module Arm
|
||||
|
||||
# TODO actually find the closest somehow (DROPPED for now)
|
||||
def self.closest_addrtable(as)
|
||||
as.values.find do |obj|
|
||||
obj.is_a?(Asm::Arm::AddrTableObject)
|
||||
end || (raise Asm::AssemblyError.new('could not find addrtable to use', nil))
|
||||
end
|
||||
|
||||
|
||||
#this has been DROPPED for now (didn't work) and we can do without it
|
||||
class AddrTableObject
|
||||
def initialize
|
||||
@table = []
|
||||
@const = []
|
||||
end
|
||||
|
||||
# TODO don't create new entry if there's already an entry for the same label/const
|
||||
def add_label(label)
|
||||
d = [label, Asm::LabelObject.new]
|
||||
@table << d
|
||||
d[1]
|
||||
end
|
||||
|
||||
def add_const(const)
|
||||
d = [const, Asm::LabelObject.new]
|
||||
@const << d
|
||||
d[1]
|
||||
end
|
||||
|
||||
def assemble(io, as)
|
||||
@table.each do |pair|
|
||||
target_label, here_label = *pair
|
||||
here_label.assemble io, as
|
||||
as.add_relocation io.tell, target_label, Asm::Arm::R_ARM_ABS32,
|
||||
Asm::Arm::Instruction::RelocHandler
|
||||
io.write_uint32 0
|
||||
end
|
||||
@const.each do |pair|
|
||||
const, here_label = *pair
|
||||
here_label.assemble io, as
|
||||
io.write_uint32 const
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
85
unused/ast_assembler.rb
Normal file
85
unused/ast_assembler.rb
Normal file
@ -0,0 +1,85 @@
|
||||
module Asm
|
||||
class AstAssembler
|
||||
def initialize(asm_arch)
|
||||
@asm_arch = asm_arch
|
||||
|
||||
@symbols = {}
|
||||
@inst_label_context = {}
|
||||
|
||||
@asm = Asm::Assembler.new
|
||||
end
|
||||
|
||||
def assembler
|
||||
@asm
|
||||
end
|
||||
|
||||
def load_ast(ast)
|
||||
label_breadcrumb = []
|
||||
ast.children.each do |cmd|
|
||||
if (cmd.is_a?(Asm::LabelNode))
|
||||
m = /^\/+/.match(cmd.name)
|
||||
count = m ? m[0].length : 0
|
||||
label_breadcrumb = label_breadcrumb[0,count]
|
||||
label_breadcrumb << cmd.name[count..-1]
|
||||
@asm.add_value object_for_label(label_breadcrumb.join('/'))
|
||||
elsif (cmd.is_a?(Asm::InstructionNode))
|
||||
inst = @asm_arch::Instruction.new(cmd, self)
|
||||
@asm.add_value inst
|
||||
@inst_label_context[inst] = label_breadcrumb
|
||||
elsif (cmd.is_a?(Asm::DirectiveNode))
|
||||
if (cmd.name == 'global')
|
||||
symbol_for_label(cmd.value)[:linkage] = Elf::Constants::STB_GLOBAL
|
||||
elsif (cmd.name == 'extern')
|
||||
object_for_label(cmd.value).extern!
|
||||
elsif (cmd.name == 'hexdata')
|
||||
bytes = cmd.value.strip.split(/\s+/).map do |hex|
|
||||
hex.to_i(16)
|
||||
end.pack('C*')
|
||||
@asm.add_value Asm::StringNode.new(bytes)
|
||||
elsif (cmd.name == "asciz")
|
||||
str = eval(cmd.value) + "\x00"
|
||||
@asm.add_value Asm::StringNode.new(str)
|
||||
elsif (defined?(Asm::Arm) and cmd.name == 'addrtable')
|
||||
@asm.add_value Asm::Arm::AddrTableObject.new
|
||||
else
|
||||
raise Asm::AssemblyError.new('unknown directive', cmd)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# instruction is user for label context
|
||||
def symbol_for_label(name, instruction=nil)
|
||||
if (instruction)
|
||||
context = @inst_label_context[instruction]
|
||||
m = /^(\/*)(.+)/.match(name)
|
||||
breadcrumb = context[0,m[1].length]
|
||||
breadcrumb << m[2]
|
||||
qual_name = breadcrumb.join('/')
|
||||
else
|
||||
qual_name = name
|
||||
end
|
||||
|
||||
if (not @symbols[qual_name])
|
||||
@symbols[name] = {:label => Asm::LabelObject.new, :linkage => Elf::Constants::STB_LOCAL, :name => qual_name}
|
||||
end
|
||||
@symbols[qual_name]
|
||||
end
|
||||
|
||||
def object_for_label(name, instruction=nil)
|
||||
symbol_for_label(name, instruction)[:label]
|
||||
end
|
||||
|
||||
def assemble(io)
|
||||
@asm.assemble io
|
||||
end
|
||||
|
||||
def symbols
|
||||
@symbols.values
|
||||
end
|
||||
|
||||
def relocations
|
||||
@asm.relocations
|
||||
end
|
||||
end
|
||||
end
|
29
unused/code_generator.rb
Normal file
29
unused/code_generator.rb
Normal file
@ -0,0 +1,29 @@
|
||||
require "asm/arm/code_generator"
|
||||
|
||||
if (__FILE__ == $0)
|
||||
gen = Asm::Arm::ArmAssembler.new
|
||||
|
||||
gen.instance_eval {
|
||||
mov r0, 5
|
||||
loop_start = label
|
||||
loop_start.set!
|
||||
subs r0, r0, 1
|
||||
bne loop_start
|
||||
mov r7, 1
|
||||
swi 0
|
||||
}
|
||||
|
||||
gen.add_string("printf"+ "\x00")
|
||||
require 'asm/object_writer'
|
||||
writer = Asm::ObjectWriter.new(Elf::Constants::TARGET_ARM)
|
||||
writer.set_text gen.assemble_to_string
|
||||
|
||||
|
||||
begin
|
||||
writer.save('arm_as_generated.o')
|
||||
rescue => err
|
||||
puts 'as: cannot save output file: ' + err.message
|
||||
exit
|
||||
end
|
||||
|
||||
end
|
149
unused/command_line.rb
Normal file
149
unused/command_line.rb
Normal file
@ -0,0 +1,149 @@
|
||||
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
|
19
unused/elf_object.rb
Normal file
19
unused/elf_object.rb
Normal file
@ -0,0 +1,19 @@
|
||||
if (__FILE__ == $0)
|
||||
obj = Elf::ObjectFile.new Elf::TARGET_ARM
|
||||
|
||||
sym_strtab = Elf::StringTableSection.new(".strtab")
|
||||
obj.add_section sym_strtab
|
||||
symtab = Elf::SymbolTableSection.new(".symtab", sym_strtab)
|
||||
obj.add_section symtab
|
||||
|
||||
text_section = Elf::TextSection.new(".text")
|
||||
obj.add_section text_section
|
||||
|
||||
symtab.add_func_symbol "_start", 0, text_section, Elf::STB_GLOBAL
|
||||
|
||||
fp = File.open("test.o", "wb")
|
||||
obj.write fp
|
||||
|
||||
fp.close
|
||||
end
|
||||
|
223
unused/parser.rb
Normal file
223
unused/parser.rb
Normal file
@ -0,0 +1,223 @@
|
||||
require_relative 'str_scanner'
|
||||
require_relative 'nodes'
|
||||
class NumEquivAddrNode < NumLiteralNode
|
||||
end
|
||||
class LabelEquivAddrNode < LabelRefNode
|
||||
end
|
||||
class ToplevelNode < Node
|
||||
attr_accessor :children
|
||||
end
|
||||
class DirectiveNode < Node
|
||||
attr_accessor :name, :value
|
||||
end
|
||||
class LabelNode < Node
|
||||
attr_accessor :name
|
||||
end
|
||||
class MathNode < Node
|
||||
attr_accessor :left, :right, :op
|
||||
alias_method :argument, :left
|
||||
alias_method :argument=, :left=
|
||||
end
|
||||
|
||||
|
||||
module Asm
|
||||
class Parser
|
||||
def initialize(str)
|
||||
scanner = Asm::Scanner.new(str)
|
||||
|
||||
@ast = parse_toplevel scanner
|
||||
end
|
||||
attr_reader :ast
|
||||
|
||||
def self.parse(str)
|
||||
new(str).ast
|
||||
end
|
||||
|
||||
def parse_toplevel(s)
|
||||
node = ToplevelNode.new(s)
|
||||
node.children = []
|
||||
while (not s.eos?)
|
||||
node.children << parse(s)
|
||||
end
|
||||
node
|
||||
end
|
||||
|
||||
def parse(s)
|
||||
s.scan /\s*/
|
||||
node = nil
|
||||
%w(comment directive label instruction).each { |em|
|
||||
if (node = send('parse_'+em, s))
|
||||
break
|
||||
end
|
||||
}
|
||||
raise Asm::ParseError.new('could not parse element', s) unless node
|
||||
s.scan /\s*/
|
||||
node
|
||||
end
|
||||
|
||||
def parse_comment(s)
|
||||
if (s.scan(/;.*?$/))
|
||||
CommentNode.new(s)
|
||||
end
|
||||
end
|
||||
|
||||
def parse_directive(s)
|
||||
if (m = s.scan(/\.(\w+)(?:(?!$)\s+(.+)\s*?$)?/))
|
||||
DirectiveNode.new(s) { |n|
|
||||
n.name = m[0]
|
||||
n.value = m[1]
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def parse_label(s)
|
||||
if (m = s.scan(/(\/*\w+):/))
|
||||
LabelNode.new(s) { |n|
|
||||
n.name = m[0]
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def parse_instruction(s)
|
||||
if (m = s.scan(/(\w+)/))
|
||||
node = InstructionNode.new(s) { |n|
|
||||
n.opcode = m[0]
|
||||
n.args = []
|
||||
}
|
||||
if (not s.scan(/\s*($|;)/))
|
||||
loop {
|
||||
arg = parse_arg(s)
|
||||
node.args << arg
|
||||
break if not s.scan(/\s*,/)
|
||||
}
|
||||
end
|
||||
node
|
||||
end
|
||||
end
|
||||
|
||||
def parse_arg(s)
|
||||
s.scan /\s*/
|
||||
node = nil
|
||||
%w(reference register register_list num_literal label_ref).each { |em|
|
||||
if (node = send('parse_'+em, s))
|
||||
break
|
||||
end
|
||||
}
|
||||
raise Asm::ParseError.new('expected argument but none found', s) unless node
|
||||
|
||||
if (node2 = parse_arg_op(s))
|
||||
node2.argument = node
|
||||
node = node2
|
||||
end
|
||||
|
||||
s.scan /\s*/
|
||||
node
|
||||
end
|
||||
|
||||
def parse_arg_op(s)
|
||||
s.scan /\s*/
|
||||
node = nil
|
||||
%w(shift math).each do |em|
|
||||
if (node = send('parse_'+em, s))
|
||||
break
|
||||
end
|
||||
end
|
||||
s.scan /\s*/
|
||||
node
|
||||
end
|
||||
|
||||
def parse_shift(s)
|
||||
if (m = s.scan(/(lsl|lsr|asr|ror|rrx)\s+/i))
|
||||
op = m[0].downcase
|
||||
if (op == 'rrx' or arg = parse_arg(s))
|
||||
ShiftNode.new(s) { |n|
|
||||
n.type = m[0].downcase
|
||||
n.value = arg
|
||||
}
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def parse_math(s)
|
||||
if (m = s.scan_str(/[\+\-]/))
|
||||
if (arg1 = parse_arg(s))
|
||||
MathNode.new(s) do |n|
|
||||
n.right = arg1
|
||||
n.op = m
|
||||
end
|
||||
else
|
||||
raise Asm::ParseError.new('expected right side for arithmetic op', s)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
REGISTER_REGEXP = Regexp.union(*%w(r0 r1 r2 r3 r4 r5 r6 r7 r8 r9 r10 r11 r12
|
||||
r13 r14 r15 a1 a2 a3 a4 v1 v2 v3 v4 v5 v6
|
||||
rfp sl fp ip sp lr pc
|
||||
))
|
||||
def parse_register(s)
|
||||
if (m = s.scan_str(REGISTER_REGEXP))
|
||||
RegisterNode.new(s) { |n|
|
||||
n.name = m
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def parse_register_list(s)
|
||||
if (m = s.scan(/\{/))
|
||||
node = RegisterListNode.new(s) do |n|
|
||||
n.registers = []
|
||||
end
|
||||
loop do
|
||||
s.scan /\s*/
|
||||
reg = parse_register(s)
|
||||
if (not reg)
|
||||
return nil
|
||||
end
|
||||
s.scan /\s*,?/
|
||||
|
||||
node.registers << reg
|
||||
|
||||
if (s.scan(/\}/))
|
||||
break
|
||||
end
|
||||
end
|
||||
node
|
||||
end
|
||||
end
|
||||
|
||||
def parse_num_literal(s)
|
||||
if (m = s.scan(/(=?)#(-?(?:0x)?[0-9A-Fa-f]+)/))
|
||||
(m[0] == '=' ? NumEquivAddrNode : NumLiteralNode).new(s) { |n|
|
||||
n.value = Integer(m[1])
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def parse_label_ref(s)
|
||||
if (m = s.scan(/(=?)(\/*\w+)/))
|
||||
(m[0] == '=' ? LabelEquivAddrNode : LabelRefNode).new(s) { |n|
|
||||
n.label = m[1]
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def parse_reference(s)
|
||||
if (m = s.scan(/\[/))
|
||||
arg = parse_arg(s)
|
||||
if (arg and s.scan(/\]/))
|
||||
ReferenceNode.new(s) do |n|
|
||||
n.argument = arg
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if (__FILE__ == $0)
|
||||
p Asm::Parser.parse ARGV[0]
|
||||
end
|
65
unused/str_scanner.rb
Normal file
65
unused/str_scanner.rb
Normal file
@ -0,0 +1,65 @@
|
||||
module AS; end
|
||||
|
||||
if (not defined? RUBY_ENGINE or not RUBY_ENGINE == 'rbx')
|
||||
class Regexp
|
||||
def match_start(str, idx)
|
||||
Regexp.compile('\A(?:'+source+')').match(str[idx..-1])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Asm::Scanner
|
||||
def initialize(str)
|
||||
@string = str
|
||||
@pos = 0
|
||||
@line = 0
|
||||
@column = 0
|
||||
end
|
||||
attr_accessor :string, :pos, :line, :column, :prev_line, :prev_column
|
||||
|
||||
def rest
|
||||
string[pos..-1]
|
||||
end
|
||||
|
||||
def advance_str(str)
|
||||
self.prev_line = line
|
||||
self.prev_column = column
|
||||
self.pos += str.length
|
||||
self.line += str.count("\n")
|
||||
if (str.include?("\n"))
|
||||
self.column = str.length - str.rindex("\n")
|
||||
else
|
||||
self.column += str.length
|
||||
end
|
||||
end
|
||||
|
||||
def scan(regexp)
|
||||
if (match = regexp.match_start(rest, 0))
|
||||
advance_str match.to_s
|
||||
match.captures
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def scan_str(regexp)
|
||||
if (match = regexp.match_start(rest, 0))
|
||||
advance_str match.to_s
|
||||
match.to_s
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def lookahead(regexp)
|
||||
if (match = regexp.match_start(rest, 0))
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def eos?
|
||||
pos == string.length
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue
Block a user