275 lines
10 KiB
Ruby
275 lines
10 KiB
Ruby
require 'intel/special_register'
|
|
|
|
module Intel
|
|
##
|
|
# Command is a potential command you can call. It has an
|
|
# opcode (eg: MOV) and the memory format that it outputs as
|
|
# (opcodes) as well as the kinds of parameters it takes and the
|
|
# processor types that support the command.
|
|
|
|
# Commands are parsed from the assembler.txt file
|
|
|
|
class Command
|
|
attr_accessor :opcode, :opcodes, :processors , :parameters
|
|
|
|
def initialize opcode , opcodes , processors , parameters = nil
|
|
@opcode = opcode
|
|
@opcodes = opcodes
|
|
@processors = processors
|
|
@parameters = parameters
|
|
end
|
|
|
|
def dup
|
|
x = super
|
|
x.parameters = x.parameters.dup
|
|
x.opcodes = x.opcodes.dup
|
|
x
|
|
end
|
|
|
|
# TODO: learn this better, and figure out why not polymorphic ==
|
|
def parameter_matches a, b
|
|
return false if String === b
|
|
|
|
if a.is_a?(Register) && b.is_a?(Register) then
|
|
return a.bits == b.bits && (b.id.nil? || a.id == b.id)
|
|
end
|
|
|
|
if a.is_a?(Address) && b.is_a?(Address) then
|
|
return ! b.offset? || a.offset?
|
|
end
|
|
|
|
if a.is_a?(SpecialRegister) && b.is_a?(SpecialRegister) then
|
|
return a.class == b.class && (b.id.nil? || a.id == b.id)
|
|
end
|
|
|
|
return false unless b.is_a?(Immediate)
|
|
|
|
if a.is_a? Integer then
|
|
return (b.value && b.value == a) || b.bits.nil? || a < (2 ** b.bits)
|
|
end
|
|
|
|
if a.is_a? Label then
|
|
return (a.is_a?(FutureLabel) && a.position.nil? )? b.bits == a.machine.bits :
|
|
a.bits <= (b.bits || a.machine.bits)
|
|
end
|
|
|
|
false
|
|
end
|
|
|
|
def instruction_applies? instruction
|
|
return false if instruction.opcode != self.opcode
|
|
return false if instruction.parameters.size != self.parameters.size
|
|
|
|
instruction.parameters.zip(self.parameters).all? { |a, b|
|
|
self.parameter_matches a, b
|
|
}
|
|
end
|
|
|
|
def to_parameter parameter
|
|
case parameter
|
|
when 'r/m8' then return parameter # "Expanded by the parser"
|
|
when 'r/m16' then return parameter # "Expanded by the parser"
|
|
when 'r/m32' then return parameter # "Expanded by the parser"
|
|
when 'r/m64' then return parameter # "Expanded by the parser"
|
|
when 'TO fpureg' then return parameter # "Fixed in nasm_fixes"
|
|
when 'SHORT imm' then return parameter # "Fixed in nasm_fixes"
|
|
when 'FAR mem' then return parameter # "Fixed in nasm_fixes"
|
|
when 'FAR mem16' then return parameter # "Fixed in nasm_fixes"
|
|
when 'FAR mem32' then return parameter # "Fixed in nasm_fixes"
|
|
when 'NEAR imm' then return parameter # "Fixed in nasm_fixes"
|
|
when 'imm:imm16' then return parameter # "Fixed in nasm_fixes"
|
|
when 'imm:imm32' then return parameter # "Fixed in nasm_fixes"
|
|
when '1' then return Immediate.new(1)
|
|
when 'AL' then return Register.new(nil, 0, 8)
|
|
when 'AX' then return Register.new(nil, 0, 16)
|
|
when 'EAX' then return Register.new(nil, 0, 32)
|
|
when 'CL' then return Register.new(nil, 1, 8)
|
|
when 'CX' then return Register.new(nil, 1, 16)
|
|
when 'ECX' then return Register.new(nil, 1, 32)
|
|
when 'DL' then return Register.new(nil, 2, 8)
|
|
when 'DX' then return Register.new(nil, 2, 16)
|
|
when 'EDX' then return Register.new(nil, 2, 32)
|
|
when 'BL' then return Register.new(nil, 3, 8)
|
|
when 'BX' then return Register.new(nil, 3, 16)
|
|
when 'EBX' then return Register.new(nil, 3, 32)
|
|
when 'ES' then return SegmentRegister.new(nil, 0)
|
|
when 'CS' then return SegmentRegister.new(nil, 1)
|
|
when 'SS' then return SegmentRegister.new(nil, 2)
|
|
when 'DS' then return SegmentRegister.new(nil, 3)
|
|
when 'FS' then return SegmentRegister.new(nil, 4)
|
|
when 'GS' then return SegmentRegister.new(nil, 5)
|
|
when 'imm' then return Immediate.new
|
|
when 'imm8' then return Immediate.new(8)
|
|
when 'imm16' then return Immediate.new(16)
|
|
when 'imm32' then return Immediate.new(32)
|
|
when 'segreg' then return SegmentRegister.new
|
|
when 'reg' then return Register.new
|
|
when 'reg8' then return Register.new(nil,nil,8)
|
|
when 'reg16' then return Register.new(nil,nil,16)
|
|
when 'reg32' then return Register.new(nil,nil,32)
|
|
when 'mem' then return Address.new(false, 4)
|
|
when 'mem8' then return Address.new(false, 8)
|
|
when 'mem16' then return Address.new(false, 16)
|
|
when 'mem32' then return Address.new(false, 32)
|
|
when 'mem64' then return Address.new(false, 64)
|
|
when 'mem80' then return Address.new(false, 80)
|
|
when 'memoffs8' then return Address.new(true, 8)
|
|
when 'memoffs16' then return Address.new(true, 16)
|
|
when 'memoffs32' then return Address.new(true, 32)
|
|
when 'fpureg' then return FPURegister.new
|
|
when /ST(.*)/ then return FPURegister.new(nil,$1.to_i)
|
|
when 'mmxreg' then return MMXRegister.new
|
|
when /MM(.*)/ then return MMXRegister.new(nil,nil,$1.to_i)
|
|
when 'CR0/2/3/4' then return ControlRegister.new
|
|
when 'DR0/1/2/3/6/7' then return DebugRegister.new
|
|
when 'TR3/4/5/6/7' then return TestRegister.new
|
|
else
|
|
warn "unknown parameter: #{parameter.inspect}"
|
|
return parameter
|
|
end
|
|
end
|
|
|
|
def initialize_parameters params
|
|
self.parameters = params.split(/,/).map { |s| self.to_parameter s }
|
|
end
|
|
|
|
def assemble instruction
|
|
stream = []
|
|
|
|
opcodes.each_with_index do |each, index|
|
|
self.execute_instruction_position_on(each, instruction,
|
|
(index + 1) / opcodes.size, stream)
|
|
end
|
|
|
|
stream
|
|
end
|
|
|
|
def execute_instruction_position_on(byte, instruction, position, stream)
|
|
case byte
|
|
when 'a16', 'a32' then
|
|
raise "not done yet"
|
|
when 'o16' then
|
|
return self.align16_on(instruction, stream)
|
|
when 'o32' then
|
|
return self.align32_on(instruction, stream)
|
|
when 'ib' then
|
|
return stream.push_B(instruction.get_immediate)
|
|
when 'iw' then
|
|
return stream.push_W(instruction.get_second_immediate) if position == 1
|
|
return stream.push_W(instruction.get_immediate)
|
|
when 'id' then
|
|
return stream.push_D(instruction.get_second_immediate) if position == 1
|
|
return stream.push_D(instruction.get_immediate)
|
|
when 'rb' then
|
|
return self.relative_b_on(instruction, stream)
|
|
when 'rw' then
|
|
return self.relative_w_on(instruction, stream)
|
|
when 'rw/rd' then
|
|
return self.relative_w_on(instruction, stream) if
|
|
instruction.machine.bits == 16
|
|
return self.relative_d_on(instruction, stream)
|
|
when 'rd' then
|
|
return self.relative_d_on(instruction, stream)
|
|
when 'ow' then
|
|
raise byte
|
|
# [^stream push_W: instruction get_address offset].
|
|
when 'od' then
|
|
raise byte
|
|
# [^stream push_D: instruction get_address offset].
|
|
when 'ow/od' then
|
|
if instruction.machine.bits == 16 then
|
|
stream.push_W instruction.get_address.offset
|
|
end
|
|
|
|
return stream.push_D(instruction.get_address.offset)
|
|
when /^\/(.*)/ then
|
|
return self.modrm_instruction_on($1, instruction, stream)
|
|
end
|
|
|
|
number = byte.hex
|
|
number += instruction.parameters[parameters.first.id ? 1 : 0].id if
|
|
byte =~ /r$/
|
|
stream << number
|
|
end
|
|
|
|
##
|
|
# If we get here, there will be at least two parameters to combine
|
|
# a memory address with a register or a register with a register"
|
|
|
|
def modrm_r_on instruction, stream
|
|
address, register = instruction.first, instruction.second
|
|
swap = false # TODO: this can be 1 call at the bottom
|
|
|
|
if instruction.first.is_a?(Register) && instruction.second.is_a?(Register) then
|
|
if parameters.first.is_a?(MemoryRegister) then
|
|
return instruction.first.push_mod_rm_on(instruction.second, stream)
|
|
else
|
|
return instruction.second.push_mod_rm_on(instruction.first, stream)
|
|
end
|
|
end
|
|
|
|
if instruction.first.is_a?(SpecialRegister) then
|
|
return instruction.second.push_mod_rm_on(instruction.first, stream)
|
|
end
|
|
|
|
if instruction.second.is_a?(SpecialRegister) then
|
|
return instruction.first.push_mod_rm_on(instruction.second, stream)
|
|
end
|
|
|
|
address, register = if instruction.first.is_a?(Register) && instruction.second.respond_to?(:push_mod_rm_on) then
|
|
[instruction.second, instruction.first]
|
|
else
|
|
[instruction.first, instruction.second]
|
|
end
|
|
|
|
address.push_mod_rm_on register, stream
|
|
end
|
|
|
|
def align16_on instruction, stream
|
|
stream << 0x66 if instruction.machine.bits != 16
|
|
end
|
|
|
|
def relative_x_on instruction, stream, msg, dist
|
|
offset = instruction.first
|
|
offset = offset.offset if offset.is_a?(Address) && offset.offset?
|
|
|
|
if offset.is_a? Label then
|
|
if (offset.is_a?(FutureLabel) && offset.position.nil? ) then
|
|
offset.add instruction.machine.stream.size
|
|
return stream.send(msg, dist)
|
|
end
|
|
offset = offset.position
|
|
end
|
|
|
|
stream.send(msg, -(instruction.machine.stream.size - offset + dist))
|
|
end
|
|
|
|
def relative_b_on instruction, stream
|
|
relative_x_on instruction, stream, :push_B, 2
|
|
end
|
|
|
|
def relative_d_on instruction, stream
|
|
relative_x_on instruction, stream, :push_D, 5
|
|
end
|
|
|
|
def relative_w_on instruction, stream
|
|
relative_x_on instruction, stream, :push_W, 3
|
|
end
|
|
|
|
def modrm_n_instruction_on id, instruction, stream
|
|
instruction.first.push_mod_rm_on Register.new(instruction.machine, id, instruction.first.bits), stream
|
|
end
|
|
|
|
def align32_on instruction, stream
|
|
stream << 0x67 if instruction.machine.bits != 32
|
|
end
|
|
|
|
def modrm_instruction_on byte, instruction, stream
|
|
if byte == "r" then
|
|
self.modrm_r_on instruction, stream
|
|
else
|
|
self.modrm_n_instruction_on byte.to_i, instruction, stream
|
|
end
|
|
end
|
|
end
|
|
end |