rubyx/lib/intel/assembler.rb

147 lines
4.3 KiB
Ruby
Raw Normal View History

require 'intel/register'
require 'intel/address'
module Intel
##
# Assembler parses the NASM documentation and creates Command
# objects for it
class Assembler
attr_accessor :commands , :raw_commands
def self.nasm_fixes
# TODO: extend parser to split /[,:]/ and remove some of these
'
CALL imm,imm16 ; o16 9A iw iw [8086]
CALL imm,imm32 ; o32 9A id iw [386]
CALLFAR mem16 ; o16 FF /3 [8086]
CALLFAR mem32 ; o32 FF /3 [386]
Jcc imm ; 0F 80+cc rw/rd [386]
JMP imm,imm16 ; o16 EA iw iw [8086]
JMP imm,imm32 ; o32 EA id iw [386]
JMP imm16 ; E9 rw/rd [8086]
JMP imm32 ; E9 rw/rd [8086]
JMP imm8 ; EB rb [8086]
JMPFAR mem16 ; o16 FF /5 [8086]
JMPFAR mem32 ; o32 FF /5 [386]
FADDTO fpureg ; DC C0+r [8086,FPU]
FDIVTO fpureg ; DC F8+r [8086,FPU]
FDIVRTO fpureg ; DC F0+r [8086,FPU]
FMULTO fpureg ; DC C8+r [8086,FPU]
FSUBTO fpureg ; DC E8+r [8086,FPU]
FSUBRTO fpureg ; DC E0+r [8086,FPU]
CMP r/m16,imm8 ; o16 83 /7 ib [8086]
CMP r/m32,imm8 ; o32 83 /7 ib [386]
SAR r/m16,1 ; o16 D1 /7 [8086]
SAR r/m32,1 ; o32 D1 /7 [386]
'
end
def self.nasm
File.read(__FILE__.sub(".rb",".txt"))
end
@@default = nil
def self.default
@@default ||= self.new.parse
end
def self.commands
self.default.commands
end
def initialize
self.commands = []
self.raw_commands = []
end
def expand_parameters command
command.parameters.each_with_index do |parameter, index|
if String === parameter && parameter =~ /^r\/m(\d+)/ then
bits = $1.to_i
newCommand = command.dup
commands << newCommand
case bits
when 8, 16, 32 then
command.parameters[index] = MemoryRegister.new bits
newCommand.parameters[index] = Address.new false, bits
when 64 then
command.parameters[index] = MMXRegister.new nil,nil,bits
newCommand.parameters[index] = Address.new false, bits
end
end
end
end
def add_conditional_commands prototype
prototype.opcode = prototype.opcode[0..-3]
self.conditionals.each do |conditional, value|
command = prototype.dup
command.opcode += conditional
command.opcodes.each_with_index do |op, index|
command.opcodes[index] = ($1.hex+value).to_s(16) if op =~ /(.*)\+cc$/
end
self.add_command command
end
end
def process_line line # TODO: remove
return if line.empty?
return unless line =~ /^[A-Z].+;.*\[/
self.parse_command line
end
def add_raw_command raw
@raw_commands << raw
end
def add_command command
return self.add_conditional_commands(command) if command.opcode =~ /cc$/i
self.commands << command
self.expand_parameters command
end
def conditionals
@conditionals ||= {
'O' => 0, 'NO' => 1, 'B' => 2, 'C' => 2, 'NAE' => 2,
'AE' => 3, 'NB' => 3, 'NC' => 3, 'E' => 4, 'Z' => 4,
'NE' => 5, 'NZ' => 5, 'BE' => 6, 'NA' => 6, 'A' => 7,
'NBE' => 7, 'S' => 8, 'NS' => 9, 'P' => 10, 'PE' => 10,
'NP' => 11, 'PO' => 11, 'L' => 12, 'NGE' => 12, 'GE' => 13,
'NL' => 13, 'LE' => 14, 'NG' => 14, 'G' => 15, 'NLE' => 15,
}
end
def parse_command line
if line =~ /^(\w+)\s+([^;]*)\s+;\s+([^\[]+)\s+\[([\w,]+)\]/ then
name, params, ops, procs = $1, $2, $3, $4
command = Command.new(name, ops.split , procs.split(/,/) )
command.initialize_parameters params.strip
self.add_raw_command command
else
raise "unparsed: #{line}"
end
end
def parse
(self.class.nasm + self.class.nasm_fixes).each_line do |line|
self.process_line line.strip.sub(/^# /, '')
end
@raw_commands.each do |command|
add_command command
end
self
end
end
end