实现8086汇编编译器(二)——汇编指令的翻译【mov 指令】

前言

因为 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 机器指令的格式如下:

在这里插入图片描述

可以看到指令共分为七大类:

  1. 寄存器/内存 到/从 寄存器
  2. 立即数到寄存器/内存
  3. 立即数到寄存器【这种编码格式比上面的更短】
  4. 内存到累加器
  5. 累加器到内存
  6. 寄存器/内存 到 段寄存器
  7. 段寄存器 到 寄存器/内存

mov 汇编指令的格式

根据 mov 机器指令格式,mov 汇编指令细分以下就有11种,【右边是源操作数,左边是目的操作数】:

  1. 寄存器到寄存器【mov ax,bx】
  2. 内存到寄存器【mov cx,[bp+si+1]】
  3. 寄存器到内存【mov [bx+si],ax】
  4. 立即数到寄存器【mov ax,123】
  5. 立即数到内存【mov [bx],123】
  6. 内存到累加器【mov ax,[si+1]】
  7. 累加器到内存【mov [si+1],al】
  8. 寄存器到段寄存器【mov cs,ax】
  9. 内存到段寄存器【mov cs,[1]】
  10. 段寄存器到寄存器【mov cx,cs】
  11. 段寄存器到内存【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,
		})
}

它会将内部标号的信息记录下来,后续处理【见后续文章】。

猜你喜欢

转载自blog.csdn.net/woay2008/article/details/126564802