文章目录
前言
因为 CPU 只能识别和执行机器指令,所以需要将汇编指令翻译【转换,编码】成机器指令。
官方手册《Intel_8086_Family_Users_Manual》里面包含了所有汇编指令对应的机器指令的格式。
这篇文件介绍mov
指令的翻译是如何实现的。
机器指令通用格式
8086 机器指令的长度从1字节到6字节不等。大部分指令的格式如下:
多字节指令的前 6 位通常包含一个操作码,用于标识基本指令类型:比如 ADD、XOR 等。
接下来的位称为 D 字段,一般指定操作的“方向”:1 = REG 字段标识目标操作数,0 = REG 字段标识源操作数。
W 字段区分字节和字操作之间:0 = 字节操作,1 = 字操作。
除了D和W外,其他的指令可能还出现以下几个字段:
指令的第 2 个字节通常标识指令的操作数。 MOD 字段指示两个操作数之一是否在内存中【也就是其中之一是否是内存操作数。因为 8086 的指令不可能同时有两个内存操作数】或者两个操作数是否都是寄存器:
REG 字段标识操作数使用哪个寄存器【寄存器操作数】:
在一些指令中,主要是立即数到内存类型的指令中, REG被用来扩展操作码来识别操作的类型。
R/M(寄存器/内存)字段的编码取决于MOD字段的设置方式。 如果 MOD = 11(寄存器到寄存器模式),那么 R/M 标识第二个寄存器操作数。 如果 MOD 是内存操作模式,那么 R/M表示内存操作数的有效地址是如何计算的:
指令的第 3 到 6 字节是可选的,通常包含内存操作数的偏移量【displacement 】和/或一个立即数的值。MOD 字段指明了偏移量的长度是 1 字节还是 2 字节【1个字】,第 2 字节是字的最高字节。
偏移量后的立即数也是可选的,第 2 字节是最高字节。
mov 机器指令的格式
mov 机器指令的格式如下:
可以看到指令共分为七大类:
- 寄存器/内存 到/从 寄存器
- 立即数到寄存器/内存
- 立即数到寄存器【这种编码格式比上面的更短】
- 内存到累加器
- 累加器到内存
- 寄存器/内存 到 段寄存器
- 段寄存器 到 寄存器/内存
mov 汇编指令的格式
根据 mov 机器指令格式,mov 汇编指令细分以下就有11种,【右边是源操作数,左边是目的操作数】:
- 寄存器到寄存器【mov ax,bx】
- 内存到寄存器【mov cx,[bp+si+1]】
- 寄存器到内存【mov [bx+si],ax】
- 立即数到寄存器【mov ax,123】
- 立即数到内存【mov [bx],123】
- 内存到累加器【mov ax,[si+1]】
- 累加器到内存【mov [si+1],al】
- 寄存器到段寄存器【mov cs,ax】
- 内存到段寄存器【mov cs,[1]】
- 段寄存器到寄存器【mov cx,cs】
- 段寄存器到内存【mov [di+1],ds】
mov 指令的翻译
识别操作数类型
要识别汇编程序中的 mov 指令是哪种格式,我们首先要识别操作数的类型。
比如 mov [bx],123,源操作数类型是立即数,目的操作数是内存操作数。
mov cx, cs,源操作数类型是段寄存器,目的操作数是【通用】寄存器。
于是我定义了如下的操作数类型:
type OperandType uint8
const (
Immediate8Operand OperandType = iota
Immediate16Operand
ImmediateLabelOperand
ImmediateOffsetLabelOperand
Reg8Operand
Reg16Operand
SegRegOperand
Mem8Operand
Mem16Operand
MemUnknownSizeOperand
InvalidOperand
)
type Operand struct {
Type OperandType // 操作数类型
Value interface{
} // 操作数的值
}
立即数
立即数我把它分成4类:
- 8 位立即数
- 16 位立即数
- 内部标号
- 带 offset 修饰的内部标号
看如下的示例程序:
assume cs:code,ds:data,ss:stack ;将cs,ds,ss分别和code,data,stack段相连
data segment
dw 0123h, 0456h, 0789h, 0abch, 0defh, 0fedh, 0cbah, 0987h
data ends
stack segment
dw 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
stack ends
code segment
start: mov ax,stack
mov ss,ax
mov sp,20h ; 将设置栈顶ss:sp指向stack:20
mov ax, data ; 将名称为"data"的段的段地址送入ax
mov ds,ax ; ds指向data段
mov bx,0 ; ds:bx指向data段中的第一个单元
mov cx,8
s0: push cs:[bx]
add bx,2
loop s0 ; 以上将代码段0~15单元总的8个字型数据依次入栈
mov bx,0
mov cx, 8
s1:pop cs:[bx]
add bx,2
loop s1 ; 以上依次出栈8个字型数据到代码段0~15单元中
mov ax,4c00h
int 21h
code ends
end start
mov ax, data ,这个 data 就是内部标号。它的值是程序加载时数据段的值,它是个立即数。
mov ax,stack 也是同理。
看如下示例程序:
assume cs:code
code segment
mov ax,4c00h
int 21h
start: mov ax,0
s: nop
nop
mov di,offset s
mov si,offset s2
mov ax,cs:[si]
mov cs:[di],ax
s0: jmp short s
s1: mov ax,0
int 21h
mov ax,0
s2: jmp short s1
nop
code ends
end start
mov di,offset s,表示将外部标号 s 的【代码段】偏移量放到 di 寄存器。这里 s 就是带 offset 的标号。
【暂不支持mov di,s 这种格式,其中 s 是数据段定义的一些数据的起始地址。也就是说 s 是个变量】
mov si,offset s2 也是同理。
用如下的结构体表示一个立即数:
type ImmediateOperand struct {
Value uint16 // 值
Width uint8 // 立即数宽度,8位或16位
IsLabel bool // 是否是内部标号
IsLabelOffset bool // 是否是内部带offset的标号
Label string // 标号的名字
}
使用 isImmediateOperand 函数判断是否是一个 8 位或 16 位立即数。
// 12345
// 0ffffh
// 123h
// 1000b
// -123h
//'a'
func isSizedImmediateOperand(s string, bitSize int) (bool, *ImmediateOperand) {
s = strings.TrimSpace(s)
if len(s) == 0 {
return false, nil
}
if bitSize == 8 {
if len(s) == 3 &&
s[0] == '\'' &&
s[2] == '\'' {
return true, &ImmediateOperand{
Value: uint16(s[1]),
Width: 8,
}
}
}
isNegative := false
if s[0] == '-' {
isNegative = true
}
base := 10
if strings.HasSuffix(s, "h") {
base = 16
s = s[:len(s)-1]
} else if strings.HasSuffix(s, "b") {
base = 2
s = s[:len(s)-1]
}
if isNegative {
v, err := strconv.ParseInt(s, base, bitSize)
if err != nil {
return false, nil
}
return true, &ImmediateOperand{
Value: uint16(v),
Width: uint8(bitSize),
}
}
v, err := strconv.ParseUint(s, base, bitSize)
if err != nil {
return false, nil
}
return true, &ImmediateOperand{
Value: uint16(v),
Width: uint8(bitSize),
}
}
func isImmediateOperand(s string) (bool, *ImmediateOperand) {
t, v := isSizedImmediateOperand(s, 8)
if t {
return t, v
}
t, v = isSizedImmediateOperand(s, 16)
if t {
return t, v
}
return false, nil
}
是否是内部标号或带 offset 的内部标号的逻辑在下文给出。
寄存器操作数
寄存器分为 8 位寄存器【al,cl,dl,bl,ah,chdh,bh】,16 位寄存器【ax,cx,dx,bx,sp,bp,si,di】,段寄存器【es,cs,ss,ds】。
用如下的结构体表示:
type RegOperand struct {
Name string // 寄存器名称
Width uint8 // 寄存器宽度
IsSegReg bool // 是否是段寄存器
}
实现了如下的函数判断寄存器类型:
var reg8BitMap = map[string]uint8{
"al": 0, //Byte Multiply, Byte Divide, Byte 1/0, Translate, Decimal Arithmetic
"cl": 1, //Variable Shift and Rotate
"dl": 2,
"bl": 3,
"ah": 4, //Byte Multiply, Byte Divide
"ch": 5,
"dh": 6,
"bh": 7,
}
var reg16BitMap = map[string]uint8{
"ax": 0, //Word Multiply, Word Divide, Word 1/0
"cx": 1, //String Operations, Loops
"dx": 2, //Word Multiply, Word Divide, Indirect 1/0
"bx": 3, //Translate
"sp": 4, //Stack Operations
"bp": 5,
"si": 6, //String Operations
"di": 7, //String Operations
}
var regSegMap = map[string]uint8{
"es": 0,
"cs": 1,
"ss": 2,
"ds": 3,
}
func isReg8Operand(s string) (bool, *RegOperand) {
if _, ok := reg8BitMap[s]; !ok {
return false, nil
}
return true, &RegOperand{
Name: s, Width: 8}
}
func isReg16Operand(s string) (bool, *RegOperand) {
if _, ok := reg16BitMap[s]; !ok {
return false, nil
}
return true, &RegOperand{
Name: s, Width: 16}
}
func isSegRegOperand(s string) (bool, *RegOperand) {
if _, ok := regSegMap[s]; !ok {
return false, nil
}
return true, &RegOperand{
Name: s, Width: 16, IsSegReg: true}
}
注意:上面定义的各种 map 对应的值不是随意定的,而是根据机器指令中 REG 字段的值定义的。
内存操作数
内存操作数是最复杂的。它一共有如下16种形式:
算上偏移量是 8 位还是 16 位,一共有 24 种形式。而且立即数还可以放 [] 外,比如
[bx+di+123] 和 123[bx+di] 和 [bx+di]123 这三种形式是一样的。编译器必须都得支持。
内存操作数用如下的结构体表示:
type MemOperand struct {
IsSingleIndex bool // 是否使用一个索引寄存器,比如[bx],[bx+123]等
SingleIndexReg string // 索引寄存器,比如"bx"
IsDoubleIndex bool // 是否使用两个索引寄存器,比如[bx+si],[bx+di]123等
DoubleIndexFirstReg string // 第一个索引寄存器,比如"bx",“bp”等
DoubleIndexSecondReg string // 第二个索引寄存器 ,比如"si","di"等
HasDisplacement bool // 是否有偏移量
DisplacementValue uint16 // 偏移量的值
DisplacementWidth uint8 // 偏移量的宽度
OperandWidth uint8 // 操作数的宽度,比如 byte ptr [bx],操作数宽度就是8
HasSegmentPrefix bool // 是否有段前缀
SegmentPrefix string // 段前缀名称
}
于是在某个深夜,我写下了项目代码中最长的字符串处理函数 isSimpleMemOperand,来判断是否是一个不带段前缀和不带 word ptr 或byte ptr修饰的内存操作数。
func isSimpleMemOperand(s string) (bool, *MemOperand) {
s = strings.TrimSpace(s)
if len(s) == 0 {
return false, nil
}
// 内存操作数必须带有 [ ]
idxL := strings.IndexByte(s, '[')
idxR := strings.IndexByte(s, ']')
if idxL < 0 || idxR < 0 {
return false, nil
}
if idxL >= idxR {
return false, nil
}
// 偏移量可能放在[]左边
hasLeftImmediate := false
var LeftImmediate *ImmediateOperand
var t bool
if idxL != 0 {
if idxR != len(s)-1 {
return false, nil
}
t, LeftImmediate = isImmediateOperand(s[:idxL])
if !t {
return false, nil
}
hasLeftImmediate = true
}
// 偏移量可能放在[]右边
hasRightImmediate := false
var RightImmediate *ImmediateOperand
if idxR != len(s)-1 {
t, RightImmediate = isImmediateOperand(s[idxR+1:])
if !t {
return false, nil
}
hasRightImmediate = true
}
var Immediate *ImmediateOperand
if hasLeftImmediate {
Immediate = LeftImmediate
}
if hasRightImmediate {
Immediate = RightImmediate
}
filedFunc := func(r rune) bool {
if r == ' ' || r == '+' {
return true
}
return false
}
// 将[]中的索引寄存器名称分离出来
fields := strings.FieldsFunc(s[idxL+1:idxR], filedFunc)
if len(fields) > 3 {
return false, nil
}
if len(fields) == 1 {
//外面有偏移量
// [SI]d8
// [DI]d8
// [BP]d8
// [BX]d8
// [SI]d16
// [DI]d16
// [BP]d16
// [BX]d16
if Immediate != nil {
if fields[0] != "si" &&
fields[0] != "di" &&
fields[0] != "bx" &&
fields[0] != "bp" {
return false, nil
}
return true, &MemOperand{
IsSingleIndex: true,
SingleIndexReg: fields[0],
HasDisplacement: true,
DisplacementValue: Immediate.Value,
DisplacementWidth: Immediate.Width,
}
}
//外面没有偏移量
//[SI]
//[DI]
//[d16]
//[BX]
if fields[0] != "si" &&
fields[0] != "di" &&
fields[0] != "bx" {
t, Immediate = isImmediateOperand(fields[0])
if !t {
return false, nil
}
return true, &MemOperand{
HasDisplacement: true,
DisplacementValue: Immediate.Value,
DisplacementWidth: Immediate.Width,
}
}
return true, &MemOperand{
IsSingleIndex: true,
SingleIndexReg: fields[0],
}
}
if len(fields) == 2 {
//外面有偏移量
// [BX + SI]d8
// [BX + DI]d8
// [BP + SI]d8
// [BP + DI]d8
// [BX + SI]d16
// [BX + DI]d16
// [BP + SI]d16
// [BP + DI]d16
if Immediate != nil {
switch fields[0] {
case "bx", "bp":
if fields[1] != "si" &&
fields[1] != "di" {
return false, nil
}
case "si", "di":
if fields[1] != "bx" &&
fields[1] != "bp" {
return false, nil
}
default:
return false, nil
}
if fields[0] == "si" || fields[0] == "di" {
fields[0], fields[1] = fields[1], fields[0]
}
return true, &MemOperand{
IsDoubleIndex: true,
DoubleIndexFirstReg: fields[0],
DoubleIndexSecondReg: fields[1],
HasDisplacement: true,
DisplacementValue: Immediate.Value,
DisplacementWidth: Immediate.Width,
}
}
// 外面没有偏移量
t, Immediate = isImmediateOperand(fields[0])
if t {
fields[0], fields[1] = fields[1], fields[0]
} else {
_, Immediate = isImmediateOperand(fields[1])
}
// [BX + SI]
// [BX + DI]
// [BP + SI]
// [BP + DI]
// [SI + d8]
// [DI + d8]
// [BP + d8]
// [BX + d8]
// [SI + d16]
// [DI + d16]
// [BP + d16]
// [BX + d16]
switch fields[0] {
case "bx", "bp":
if fields[1] != "si" &&
fields[1] != "di" &&
Immediate == nil {
return false, nil
}
case "si", "di":
if fields[1] != "bx" &&
fields[1] != "bp" &&
Immediate == nil {
return false, nil
}
default:
return false, nil
}
if Immediate != nil {
return true, &MemOperand{
IsSingleIndex: true,
SingleIndexReg: fields[0],
HasDisplacement: true,
DisplacementValue: Immediate.Value,
DisplacementWidth: Immediate.Width,
}
}
return true, &MemOperand{
IsDoubleIndex: true,
DoubleIndexFirstReg: fields[0],
DoubleIndexSecondReg: fields[1],
}
}
if len(fields) == 3 {
if Immediate != nil {
return false, nil
}
t, Immediate = isImmediateOperand(fields[0])
if t {
fields[0], fields[2] = fields[2], fields[0]
} else {
t, Immediate = isImmediateOperand(fields[1])
if t {
fields[1], fields[2] = fields[2], fields[1]
} else {
t, Immediate = isImmediateOperand(fields[2])
if !t {
return false, nil
}
}
}
if fields[0] != "bx" &&
fields[0] != "bp" {
fields[0], fields[1] = fields[1], fields[0]
}
// [BX + SI + d8]
// [BX + DI + d8]
// [BP + SI + d8]
// [BP + DI + d8]
// [BX + SI + d16]
// [BX + DI + d16]
// [BP + SI + d16]
// [BP + DI + d16]
switch fields[0] {
case "bx", "bp":
if fields[1] != "si" &&
fields[1] != "di" {
return false, nil
}
case "si", "di":
if fields[1] != "bx" &&
fields[1] != "bp" {
return false, nil
}
default:
return false, nil
}
}
return true, &MemOperand{
IsDoubleIndex: true,
DoubleIndexFirstReg: fields[0],
DoubleIndexSecondReg: fields[1],
HasDisplacement: true,
DisplacementValue: Immediate.Value,
DisplacementWidth: Immediate.Width,
}
}
然后实现了 isMemOperand 来完整判断是否是一个内存操作数:
func isMemOperand(s string) (bool, *MemOperand) {
s = strings.TrimSpace(s)
idxCol := strings.IndexByte(s, ':')
if idxCol == 0 {
return false, nil
}
var t bool
var memOperand *MemOperand
var operandWidth uint8
var segPrefix string
// word ptr ds:[0]
// word ptr ds:[bx+2]
// ds:[bx+si]
if idxCol > 0 {
// have segment override prefix
idxSpace := strings.LastIndexByte(s[:idxCol], ' ')
if idxSpace == 0 {
return false, nil
}
if idxSpace > 0 {
// have word ptr or byte ptr
fields := strings.Fields(s[:idxSpace])
if fields[1] != "ptr" {
return false, nil
}
if fields[0] == "word" {
operandWidth = 16
} else if fields[0] == "byte" {
operandWidth = 8
} else {
return false, nil
}
}
segPrefix = s[idxSpace+1 : idxCol]
if !isSegReg(segPrefix) {
return false, nil
}
t, memOperand = isSimpleMemOperand(s[idxCol+1:])
if !t {
return false, nil
}
fmt.Printf("has seg prefix :%s\n", segPrefix)
memOperand.OperandWidth = operandWidth
memOperand.HasSegmentPrefix = true
memOperand.SegmentPrefix = segPrefix
return true, memOperand
}
//no segment override prefix
// word ptr [bx+2]
//[bx+2]
fields := strings.Fields(s)
if fields[0] == "word" ||
fields[0] == "byte" {
if fields[1] != "ptr" {
return false, nil
}
idxP := strings.IndexByte(s, 'p')
if idxP <= 0 {
return false, nil
}
if fields[0] == "word" {
operandWidth = 16
} else {
operandWidth = 8
}
t, memOperand = isSimpleMemOperand(s[idxP+3:])
if !t {
return false, nil
}
memOperand.OperandWidth = operandWidth
return t, memOperand
}
return isSimpleMemOperand(s)
}
解析操作数
当实现以上操作数的判断之后,就可以实现一个 getOperand 函数识别到所有类型的操作数了:
func getOperand(operand string) Operand {
// 是否是寄存器操作数
if t, v := isReg8Operand(operand); t {
return Operand{
Reg8Operand, v}
}
if t, v := isReg16Operand(operand); t {
return Operand{
Reg16Operand, v}
}
if t, v := isSegRegOperand(operand); t {
return Operand{
SegRegOperand, v}
}
// 是否是立即数
if t, v := isImmediateOperand(operand); t {
if v.Width == 8 {
return Operand{
Immediate8Operand, v}
} else {
return Operand{
Immediate16Operand, v}
}
}
// 是否是内存操作数
if t, v := isMemOperand(operand); t {
if v.OperandWidth == 16 {
return Operand{
Mem16Operand, v}
} else if v.OperandWidth == 8 {
return Operand{
Mem8Operand, v}
} else {
return Operand{
MemUnknownSizeOperand, v}
}
}
// 是否是带offset的内部标号
fields := strings.Fields(operand)
if len(fields) == 2 && fields[0] == "offset" {
return Operand{
ImmediateOffsetLabelOperand,
&ImmediateOperand{
IsLabelOffset: true,
Label: fields[1],
},
}
}
// 以上类型的操作数都不是,那么就是内部标号了
if len(fields) == 1 {
return Operand{
ImmediateLabelOperand,
&ImmediateOperand{
IsLabel: true,
Label: fields[0],
},
}
}
return Operand{
InvalidOperand, nil}
}
checkMov 的实现
checkMov 检查 mov 指令格式是否正确,如果正常将操作数保存到 ctx 中传给encodeMov 函数:
func checkMov(stmt []string) (bool, context.Context) {
if len(stmt) != 3 {
log.Fatal("invalid \"mov\" syntax")
}
dstOperand := getOperand(stmt[1])
dstOperandType := dstOperand.Type
srcOperand := getOperand(stmt[2])
srcOperandType := srcOperand.Type
//语义检查
if dstOperandType == InvalidOperand ||
srcOperandType == InvalidOperand {
log.Fatal("invalid mov 0:无效的操作数类型")
}
if dstOperandType <= ImmediateOffsetLabelOperand {
log.Fatal("invalid mov 1:目的操作数不能为立即数")
} else if dstOperandType == SegRegOperand {
if srcOperandType <= ImmediateOffsetLabelOperand {
log.Fatal("invalid mov 2:不能将立即数移到段寄存器")
}
if srcOperandType == SegRegOperand {
log.Fatal("invalid mov 3:不能将段寄存器移到段寄存器")
}
} else if dstOperandType >= Mem8Operand {
if srcOperandType >= Mem8Operand {
log.Fatal("invalid mov 4:不能将内存移到内存")
}
}
//判断操作数类型
if dstOperandType == Reg8Operand {
if srcOperandType == Immediate16Operand ||
srcOperandType == Reg16Operand ||
srcOperandType == SegRegOperand ||
srcOperandType == Mem16Operand {
log.Fatal("invalid mov 5:目的寄存器是8位,而源操作数是16位")
}
} else if dstOperandType == Reg16Operand ||
dstOperandType == SegRegOperand {
if srcOperandType == Reg8Operand ||
srcOperandType == Mem8Operand {
log.Fatal("invalid mov 6:目的寄存器是16位,而源操作数是8位")
}
} else if dstOperandType == Mem8Operand {
if srcOperandType == Immediate16Operand ||
srcOperandType == Reg16Operand ||
srcOperandType == SegRegOperand {
log.Fatal("invalid mov 7:目的内存操作数是8位,而源操作数是16位")
}
} else if dstOperandType == Mem16Operand {
if srcOperandType == Reg8Operand {
log.Fatal("invalid mov 8:目的内存操作数是16位,而源寄存器是8位")
}
} else if dstOperandType == MemUnknownSizeOperand {
if srcOperandType <= ImmediateOffsetLabelOperand {
log.Fatal("invalid mov 9:目的内存操作数宽度未知,而源操作数是立即数,需指明内存操作宽度!")
}
}
var k encodeCtxKey
ctx := context.Background()
k = encodeCtxKey("dst")
ctx = context.WithValue(ctx, k, dstOperand)
k = encodeCtxKey("src")
ctx = context.WithValue(ctx, k, srcOperand)
return true, ctx
}
其中主要是对 mov 指令操作数的合法性进行检查。比如:
-
目的寄存器是8位,源操作数也只能是8位。
-
目的操作数不能是立即数。
-
不能将立即数移到段寄存器。
-
等等
encodeMov 的实现
encodeMov 对将汇编指令翻译成机器指令:
func encodeMov(ctx context.Context) []byte {
var instruction []byte
dstOperand := ctx.Value(encodeCtxKey("dst")).(Operand)
dstOperandType := dstOperand.Type
srcOperand := ctx.Value(encodeCtxKey("src")).(Operand)
srcOperandType := srcOperand.Type
switch srcOperandType {
case Immediate8Operand, Immediate16Operand,
ImmediateLabelOperand, ImmediateOffsetLabelOperand:
switch dstOperandType {
case Reg8Operand, Reg16Operand:
src := srcOperand.Value.(*ImmediateOperand)
dst := dstOperand.Value.(*RegOperand)
fmt.Println("1 mov immediate to reg")
instruction = encodeMovImmediateToReg(src, dst)
case Mem8Operand, Mem16Operand, MemUnknownSizeOperand:
src := srcOperand.Value.(*ImmediateOperand)
dst := dstOperand.Value.(*MemOperand)
fmt.Println("2 mov immediate to memory")
instruction = encodeMovImmediateToMemory(src, dst)
}
case Reg8Operand, Reg16Operand:
switch dstOperandType {
case Reg8Operand, Reg16Operand:
src := srcOperand.Value.(*RegOperand)
dst := dstOperand.Value.(*RegOperand)
fmt.Println("3 mov reg to reg")
instruction = encodeMovRegToReg(src, dst)
case SegRegOperand:
src := srcOperand.Value.(*RegOperand)
dst := dstOperand.Value.(*RegOperand)
fmt.Println("4 mov reg to seg")
instruction = encodeMovRegToSeg(src, dst)
case Mem8Operand, Mem16Operand, MemUnknownSizeOperand:
src := srcOperand.Value.(*RegOperand)
dst := dstOperand.Value.(*MemOperand)
if src.Name == Accumulator16 || src.Name == Accumulator8 {
if !dst.IsSingleIndex &&
!dst.IsDoubleIndex &&
dst.HasDisplacement {
fmt.Println("5 mov accumulator to memory")
instruction = encodeMovAccumulatorToMemory(src, dst)
} else {
fmt.Println("5.1 mov reg to memory")
fmt.Println(src)
instruction = encodeMovRegToMemory(src, dst)
}
} else {
fmt.Println("6 mov reg to memory")
instruction = encodeMovRegToMemory(src, dst)
}
}
case SegRegOperand:
switch dstOperandType {
case Reg16Operand:
src := srcOperand.Value.(*RegOperand)
dst := dstOperand.Value.(*RegOperand)
fmt.Println("7 mov seg to reg")
instruction = encodeMovSegToReg(src, dst)
case Mem16Operand, MemUnknownSizeOperand:
src := srcOperand.Value.(*RegOperand)
dst := dstOperand.Value.(*MemOperand)
fmt.Println("8 mov seg to memory")
instruction = encodeMovSegToMemory(src, dst)
}
case Mem8Operand, Mem16Operand, MemUnknownSizeOperand:
switch dstOperandType {
case Reg8Operand, Reg16Operand:
src := srcOperand.Value.(*MemOperand)
dst := dstOperand.Value.(*RegOperand)
var ok bool
if dst.Name == Accumulator16 || dst.Name == Accumulator8 {
if !src.IsSingleIndex &&
!src.IsDoubleIndex &&
src.HasDisplacement {
fmt.Println("9 mov memory to accumulator")
instruction = encodeMovMemoryToAccumulator(src, dst)
ok = true
}
}
if !ok {
fmt.Println("10 mov memory to reg")
instruction = encodeMovMemoryToReg(src, dst)
}
case SegRegOperand:
src := srcOperand.Value.(*MemOperand)
dst := dstOperand.Value.(*RegOperand)
fmt.Println("11 mov memory to seg")
instruction = encodeMovMemoryToSeg(src, dst)
}
}
return instruction
}
它根据操作数的类型,调用相应的翻译函数。比如将立即数移动到内存,它调用 encodeMovImmediateToMemory 函数:
func encodeMovImmediateToMemory(src *ImmediateOperand, dst *MemOperand) []byte {
/*1100011w, mod 000 rm, [disp-lo] [disp-hi] data [data]*/
var instruction []byte
var w uint8
if dst.OperandWidth == 8 {
w = 0
} else {
w = 1
}
if dst.HasSegmentPrefix {
instruction = append(instruction, encodeSegPrefix(dst.SegmentPrefix)...)
}
instruction = append(instruction, 0b11000110|w)
instruction = append(instruction, encodeMemoryOperand(dst)...)
//考虑立即数是个label,或者是 offset label
if src.IsLabel || src.IsLabelOffset {
putLabelEncodeInfo(src.Label, uint8(len(instruction)), dst.OperandWidth, src.IsLabelOffset)
}
instruction = append(instruction, byte(src.Value))
if w == 1 {
instruction = append(instruction, byte((src.Value>>8)&0xff))
}
return instruction
}
这个函数就是按照手册将指令编码成特定的格式:
其中调用 encodeMemoryOperand 函数编码内存操作数:
func getMODAndRM(operand *MemOperand) (MOD uint8, RM uint8) {
if operand.IsDoubleIndex {
MOD = 0b00
if operand.DoubleIndexFirstReg == "bx" &&
operand.DoubleIndexSecondReg == "si" {
RM = 0b000
} else if operand.DoubleIndexFirstReg == "bx" &&
operand.DoubleIndexSecondReg == "di" {
RM = 0b001
} else if operand.DoubleIndexFirstReg == "bp" &&
operand.DoubleIndexSecondReg == "si" {
RM = 0b010
} else if operand.DoubleIndexFirstReg == "bp" &&
operand.DoubleIndexSecondReg == "di" {
RM = 0b011
}
if operand.HasDisplacement {
if operand.DisplacementWidth == 8 {
MOD = 0b01
} else {
MOD = 0b10
}
}
return
}
if operand.IsSingleIndex {
MOD = 0b00
if operand.SingleIndexReg == "si" {
RM = 0b100
} else if operand.SingleIndexReg == "di" {
RM = 0b101
} else if operand.SingleIndexReg == "bx" {
RM = 0b111
} else if operand.SingleIndexReg == "bp" {
RM = 0b110
}
if operand.HasDisplacement {
if operand.DisplacementWidth == 8 {
MOD = 0b01
} else {
MOD = 0b10
}
} else {
if RM == 0b110 {
log.Fatal("getMODAndRM error 1!")
}
}
return
}
if operand.HasDisplacement {
RM = 0b110
MOD = 0b00
} else {
log.Fatal("getMODAndRM error 2!")
}
return
}
func encodeMemoryOperand(operand *MemOperand) []byte {
/* mod 000 r/m, (DISP-LO), (DISP-HI) */
var instruction []byte
mod, rm := getMODAndRM(operand)
instruction = append(instruction, rm|uint8(mod<<6))
if operand.HasDisplacement {
instruction = append(instruction, byte(operand.DisplacementValue))
if operand.DisplacementWidth == 16 {
instruction = append(instruction, byte((operand.DisplacementValue>>8)&0xff))
} else {
/* mov bx, [1],DISP也编码成2字节,否则解码时不知道DISP长度*/
if mod == 0b00 && rm == 0b110 {
instruction = append(instruction, 0)
}
}
}
return instruction
}
关键点
值得注意的还有两点:
-
内存操作数可能带有段前缀,比如 “es: [bx]”。encodeMovImmediateToMemory 对带段前缀的内存操作数调用 encodeSegPrefix 将段前缀编码。
func encodeSegPrefix(segPrefix string) []byte { var instruction []byte var b byte switch segPrefix { case "es": b = 0b00100110 case "cs": b = 0b00101110 case "ss": b = 0b00110110 case "ds": b = 0b00111110 default: log.Fatalf("invalid seg prefix \"%s\"\n", segPrefix) } instruction = append(instruction, b) return instruction }
-
对内部标号和带 offset 的标号进行了处理。
//考虑立即数是个label,或者是 offset label if src.IsLabel || src.IsLabelOffset { putLabelEncodeInfo(src.Label, uint8(len(instruction)), dst.OperandWidth, src.IsLabelOffset) }
其中 putLabelEncodeInfo 的实现如下:
var labelEncodeInfos []LabelEncodeInfo
type LabelEncodeInfo struct {
Name string // 标号名称
Offset uint32 // 标号在程序中的偏移量
Width uint8 // 标号值的宽度
IsOffsetLabel bool // 是否是 offset 标号
IsJmpLable bool // 是否是 jmp 指令中的标号
JmpInc uint16 // jmp 指令下一条指令在代码段中的偏移量
}
func putLabelEncodeInfo(name string, offsetInInstruction uint8, width uint8, isOffsetLabel bool) {
labelEncodeInfos = append(labelEncodeInfos,
LabelEncodeInfo{
Name: name,
Offset: progOffset + uint32(offsetInInstruction),
Width: width,
IsOffsetLabel: isOffsetLabel,
})
}
它会将内部标号的信息记录下来,后续处理【见后续文章】。