Removed duplication thanks to caliper @ http://devver.net

This commit is contained in:
sjs 2009-10-20 18:04:10 -07:00
parent dd297479f6
commit de366ea542
6 changed files with 75 additions and 105 deletions

View file

@ -616,7 +616,7 @@ module Assembler
def dec(op)
if register?(op)
# dec r16 / dec r32
# dec reg32
asm { emit_byte(0x48 + op.regnum) }
else
raise "unsupported DEC instruction, op=#{op.inspect}"
@ -655,6 +655,7 @@ module Assembler
raise "unsupported AND instruction: dest=#{dest.inspect}, src=#{src.inspect}"
end
end
alias_method :and, :and_
def xor(dest, src)
@ -674,6 +675,7 @@ module Assembler
def not_(op)
group3(op, 2, 'NOT')
end
alias_method :not, :not_
def neg(op)
@ -816,6 +818,7 @@ module Assembler
emit_byte(delta)
end
end
alias_method :loop, :loop_
# Opcode group #3. 1-byte opcode, 1 operand (r/m8 or r/m32).

View file

@ -304,10 +304,9 @@ if $0 == __FILE__
puts MachHeader::MemberSizes.inspect
puts "# of MachHeader members: " + MachHeader.size.to_s + ", size in bytes: " + MachHeader.bytesize.to_s
mh = MachHeader.new(0xfeedface, 7, 3, "foobar")
puts "magic(#{MachHeader.sizeof(:magic)}): " + mh[:magic].inspect
puts "cputype(#{MachHeader.sizeof(:cputype)}): " + mh[:cputype].inspect
puts "cpusubtype(#{MachHeader.sizeof(:cpusubtype)}): " + mh[:cpusubtype].inspect
puts "segname(#{MachHeader.sizeof(:segname)}): " + mh[:segname].inspect
%w[magic, cputype, cpusubtype, segname].each do |field|
puts "#{field}(#{MachHeader.sizeof(field.to_sym)}): " + mh[field.to_sym].inspect
end
puts mh.pack_pattern.inspect
binstr = mh.serialize
puts "values: " + mh.values.inspect
@ -317,4 +316,4 @@ if $0 == __FILE__
puts "serialized: " + binstr.inspect
puts "unserialized: " + newbinstr.inspect
puts "new == old ? " + (newbinstr == binstr).to_s
end
end

View file

@ -131,56 +131,46 @@ module Assembler
end
# Basis for #data, #const, and #bss methods.
def segment_based_on_filetype(segname, options={})
unless @current_segment
permissions = VM_PROT_READ
permisions |= VM_PROT_WRITE if options.delete(:writable)
segment(segname_based_on_filetype(segname)) do |seg|
seg[:initprot] = seg[:maxprot] = permissions
end
end
yield if block_given?
return self
end
# Define a standard data section under the current segment (if present).
# This behaves similarly to the text method.
#
def data(data, sectname='__data', segname='__DATA')
unless @current_segment
segment(segname_based_on_filetype(segname)) do |seg|
seg[:maxprot] = VM_PROT_READ | VM_PROT_WRITE
seg[:initprot] = VM_PROT_READ | VM_PROT_WRITE
end
segment_based_on_filetype(segname, :writable => true) do
section(sectname, segname, data)
end
section(sectname, segname, data)
return self
end
# Define a standard const section under the current segment (if present).
# This behaves similarly to the data method.
#
def const(data, sectname='__const', segname='__DATA')
unless @current_segment
segment(segname_based_on_filetype(segname)) do |seg|
seg[:maxprot] = VM_PROT_READ
seg[:initprot] = VM_PROT_READ
end
segment_based_on_filetype(segname) do
section(sectname, segname, data)
end
section(sectname, segname, data)
return self
end
# Define a standard BSS section under the current segment (if present).
# This behaves similarly to the data method but accepts a VM size instead
# of a blob, and no data is written to file since this section is for
# uninitialized data.
#
def bss(vmsize, sectname='__bss', segname='__DATA')
unless @current_segment
segment(segname_based_on_filetype(segname)) do |seg|
seg[:maxprot] = VM_PROT_READ | VM_PROT_WRITE
seg[:initprot] = VM_PROT_READ | VM_PROT_WRITE
end
segment_based_on_filetype(segname, :writable => true) do
section(sectname, segname, '', vmsize)
end
section(sectname, segname, '', vmsize)
return self
end

View file

@ -18,37 +18,22 @@ module Assembler
return 0x2800
end
def all_symbols
symbols = []
# Functions (section #1, __text)
#
# All labels are exported. This should be changed and only functions exported!
# TODO fixme ...
#
def make_symbols(vars, type, segnum)
# Note: Sorting a Ruby hash gives an alist, e.g. [[<key>, <value>], ...]
# We can use map on it as if it were a hash so it works nicely.
#
symbols +=
@labels.sort { |a,b| a[1] <=> b[1] }.
map do |name,addr|
MachOSym.new(name, N_SECT | N_EXT, 1, 0, addr)
end
# Constants (section #2, __const)
symbols += @consts.sort { |a,b| a[1] <=> b[1] }.
map do |name, addr|
MachOSym.new(name, N_SECT, 2, 0, addr)
end
# Variables (section #3, __bss)
#
# TODO FIXME the last var exported ends up after main somewhere... WTF?!
symbols += @vars.sort { |a,b| a[1] <=> b[1] }.
map do |name, addr|
MachOSym.new(name, N_SECT, 3, 0, addr)
end
vars.sort { |a,b| a[1] <=> b[1] }.
map do |name, addr|
MachOSym.new(name, type, segnum, 0, addr)
end
end
def all_symbols
# TODO FIXME:
# - the last var exported ends up after main somewhere... WTF?!
# - All labels are exported. This should be changed and only functions exported!
symbols = make_symbols(@labels, N_SECT | N_EXT, 1) + # Functions (section #1, __text)
make_symbols(@consts, N_SECT, 2) + # Constants (section #2, __const)
make_symbols(@vars, N_SECT, 3) # Variables (section #3, __bss)
return symbols
end

View file

@ -49,16 +49,21 @@ rescue ParseError => e
exit(1)
end
def run_and_warn_on_failure(command, name)
output = `#{command}`
if $?.exitstatus != 0
puts
print output
name = command.split.first
raise "#{name} failed: #{$?.exitstatus}"
end
end
# assemble using nasm, return resulting filename.
def assemble(filename, binformat='elf')
f = base(filename)
outfile = "#{f}.o"
output = `nasm -f #{binformat} -g -o #{outfile} #{filename} 2>&1`
if $?.exitstatus != 0
puts
print output
raise "nasm failed: #{$?.exitstatus}"
end
run_and_warn_on_failure("nasm -f #{binformat} -g -o #{outfile} #{filename} 2>&1")
return outfile
end
@ -71,12 +76,7 @@ def link(filename, platform='linux')
else
raise "unsupported platform: #{platform}"
end
output = `#{cmd} #{args} -o #{f} #{filename} 2>&1`
if $?.exitstatus != 0
puts
print output
raise "ld failed: #{$?.exitstatus}"
end
run_and_warn_on_failure("#{cmd} #{args} -o #{f} #{filename} 2>&1")
`chmod u+x #{f}`
return f
end

View file

@ -190,25 +190,23 @@ class Compiler
# bit expressions #
###################
def bitor_expr
match('|')
term
def bit_expr(op, token)
match(token)
if block_given? yield else term end
asm.pop(EBX)
asm.or_(EAX, EBX)
asm.send(op, EAX, EBX)
end
def bitand_expr
match('&')
signed_factor
asm.pop(EBX)
asm.and_(EAX, EBX)
def bitor_expr
bit_expr(:or, '|')
end
def xor_expr
match('^')
term
asm.pop(EBX)
asm.xor(EAX, EBX)
bit_expr(:xor, '^')
end
def bitand_expr
bit_expr(:and, '&') { signed_factor }
end
@ -323,6 +321,9 @@ class Compiler
# true (-1) if the difference was below zero and false (0)
# otherwise (using JL, jump if less than).
def cmp_relation(a, b, options={})
expression
asm.pop(EBX)
# Invert the sense of the test?
invert = options[:invert]
@ -347,8 +348,6 @@ class Compiler
#
# if a > b then b - a < 0
def gt_relation
expression
asm.pop(EBX)
cmp_relation(EAX, EBX) # b - a
end
@ -357,8 +356,6 @@ class Compiler
#
# if a < b then a - b < 0
def lt_relation
expression
asm.pop(EBX)
cmp_relation(EBX, EAX) # a - b
end
@ -367,8 +364,6 @@ class Compiler
#
# if a >= b then !(a < b)
def ge_relation
expression
asm.pop(EBX)
# Compare them as in less than but invert the result.
cmp_relation(EBX, EAX, :invert => true)
end
@ -378,8 +373,6 @@ class Compiler
#
# if a <= b then !(a > b)
def le_relation
expression
asm.pop(EBX)
# Compare them as in greater than but invert the result.
cmp_relation(EAX, EBX, :invert => true)
end
@ -474,20 +467,20 @@ class Compiler
asm.emit_label(end_label)
end
def while_stmt
simple_loop('while') do |end_label|
def condition_loop(name, jump_instruction)
simple_loop(name) do |end_label|
condition
skip_any_whitespace
asm.jz(end_label)
asm.send(jump_instruction, end_label)
end
end
def while_stmt
condition_loop('while', :jz) # done when == 0 (falsish)
end
def until_stmt
simple_loop('until') do |end_label|
condition
skip_any_whitespace
asm.jnz(end_label)
end
condition_loop('until', :jnz) # done when != 0 (truthy)
end
def repeat_stmt