147 lines
4.3 KiB
Ruby
147 lines
4.3 KiB
Ruby
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 |