From 81b9e232cd9d989739b7b5bda8c1df12f14d40ba Mon Sep 17 00:00:00 2001 From: chai2010 Date: Sat, 2 Jun 2018 18:04:10 +0800 Subject: [PATCH] ch3-02: done --- SUMMARY.md | 2 +- ch3-asm/ch3-02-arch.md | 48 +++++++----------------------------------- 2 files changed, 9 insertions(+), 41 deletions(-) diff --git a/SUMMARY.md b/SUMMARY.md index 3e418cd..77caacb 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -24,7 +24,7 @@ * [2.12. 补充说明](ch2-cgo/ch2-12-faq.md) * [第三章 汇编语言](ch3-asm/readme.md) * [3.1. 快速入门](ch3-asm/ch3-01-basic.md) - * [3.2. 计算机结构(Doing)](ch3-asm/ch3-02-arch.md) + * [3.2. 计算机结构](ch3-asm/ch3-02-arch.md) * [3.3. 常量和变量(TODO)](ch3-asm/ch3-03-const-and-var.md) * [3.4. 函数(TODO)](ch3-asm/ch3-04-func.md) * [3.5. 控制流(TODO)](ch3-asm/ch3-05-control-flow.md) diff --git a/ch3-asm/ch3-02-arch.md b/ch3-asm/ch3-02-arch.md index 2a1c4b8..d41b90d 100644 --- a/ch3-asm/ch3-02-arch.md +++ b/ch3-asm/ch3-02-arch.md @@ -1,4 +1,4 @@ -# 3.2. 计算机结构(Doing) +# 3.2. 计算机结构 汇编语言是直面计算机的编程语言,因此理解计算机结构是掌握汇编语言的前提。当前流行的计算机基本采用的是冯·诺伊曼计算机体系结构(在某些特殊领域还有哈佛体系架构)。冯·诺依曼结构也称为普林斯顿结构,采用的是一种将程序指令和数据存储在一起的存储结构。冯·诺伊曼计算机中的指令和数据存储器其实指的是计算机中的内存,然后在配合CPU处理器就组成了一个最简单的计算机了。 @@ -86,49 +86,17 @@ X86其实是是80X86的简称(后面三个字母),包括Intel 8086、80286 很多汇编语言的教程都会强调汇编语言是不可移植的。严格来说很多汇编语言在不同的CPU类型、或不同的操作系统环境、或不同的汇编工具链下是不可移植的。而这种不可移植性正是汇编语言普及的一个极大的障碍。虽然CPU指令集的差异是导致不好移植的较大因素,但是汇编语言的相关工具链对此也有不可推卸的责任。而源自Plan9的Go汇编语言对此做了一定的改进:首先Go汇编语言在相同CPU架构上是完全一致的,也就是屏蔽了操作系统的差异;同时Go汇编语言将一些基础并且类似的指令抽象为相同名字的伪指令,从而减少不同CPU架构下汇编代码的差异(当然,寄存器名字和数量的差异是一直存在的)。本节的目的也是找出一个较小的精简指令集,以简化Go汇编语言学习的目的。 -X86是一个极其复杂的系统,有人统计x86-64中指令有将近一千个之多。不仅仅如此,X86中的很多单个指令的功能也非常强大,比如有论文证明了仅仅一个MOV指令就可以构成一个图灵完备的系统。以上这是两种极端情况,太多的指令和太少的指令都不利于汇编程序的编写。因此我们将尝试精简出一个X86-64指令集,以便于Go汇编语言的学习。 +寄存器是CPU中最重要的资源,每个要处理的内存数据原则上需要先放到寄存器中才能由CPU处理,同时寄存器中处理完的结果需要再存入内存。X86中除了状态寄存器和指令指令两个特殊的寄存器外,还有AX、BX、CX、DX、SI、DI、BP、SP几个通用寄存器。在X86-64中又增加了八个以R8-R15方式命名的通用寄存器。因为历史的原因R0-R7并不是通用寄存器,它们只是X87开始引入的MMX指令专有的寄存器。在通用寄存器中BP和SP是两个比较特殊的寄存器:其中BP用于记录当前函数帧的开始位置,和函数调用相关的指令会隐式地影响SP的值;SP则对应当前栈指针的位置,和栈相关的指令会隐式地影响SP的值。 -再次之前我们件简单看下X86和X86-64的结构图: +X86是一个极其复杂的系统,有人统计x86-64中指令有将近一千个之多。不仅仅如此,X86中的很多单个指令的功能也非常强大,比如有论文证明了仅仅一个MOV指令就可以构成一个图灵完备的系统。以上这是两种极端情况,太多的指令和太少的指令都不利于汇编程序的编写。通用的基础机器指令大概可以分为数据传输指令、算术运算和逻辑运算指令、控制流指令等几类。因此我们将尝试精简出一个X86-64指令集,以便于Go汇编语言的学习。 -![](../images/ch3-02-x86.jpg) +基础的数据传输指令有MOV、LEA、PUSH、POP等几个。其中MOV指令可以用于将字面值移动到寄存器、字面值移到内存、寄存器之间的数据传输、寄存器和内存之间的数据传输。需要注意的是,MOV传输指令的内存操作数只能有一个,可以通过某个临时寄存器要达到类似目的。LEA指令将标参数准格式中的内存地址加载到寄存器(而不是加载内存位置的内容)。PUSH和POP分别是压栈和出栈指令,通用寄存器中的SP为栈指针,栈是向低地址方向增长的。 -![](../images/ch3-02-x86-64.jpg) +基础算术指令有ADD、SUB、MUL、DIV等指令。其中ADD、SUB、MUL、DIV用于加、减、乘、除运算,最终结果存入目标寄存器。基础的逻辑运算指令有AND、OR和NOT等几个指令,对应逻辑与、或和取反等几个指令。 - +控制流指令有CMP、JMP-if-x、JMP、CALL、RET等指令。CMP指令用于两个操作数做减法,根据比较结果设置状态寄存器的符号位和零位,可以用于有条件跳转的跳转条件。JMP-if-x是一组有条件跳转指令,常用的有JL、JLZ、JE、JNE、JG、JGE等指令,对应小于、小于等于、等于、不等于、大于和大于等于等条件时跳转。JMP指令则对应无条件跳转,将要跳转的地址设置到IP指令寄存器就实现了跳转。而CALL和RET指令分别为调用函数和函数返回指令。 -其中最重要的部分是指令指针、标题标志和通用寄存器部分。对比可以发现X86和X86-64结构还是比较相似的,只是64位CPU的通用寄存器更多一些而已。因为历史原因,X86中的前8个通用寄存器并不是以数字顺序命名,它们是以AX、BX、CX、DX、SI、DI、BP、SP方式命名。而X86-64新增加的8个通用寄存器则是以R8-R15方式命名。其中R0-R7并不是通用寄存器,它们之上X87开始引入的MMX指令专有的寄存器。在通用寄存器中BP和SP是两个比较特殊的寄存器:其中BP用于记录当前函数帧的开始位置,和函数调用相关的指令会隐式地影响SP的值;SP则对应当前栈指针的位置,和栈相关的指令会隐式地影响SP的值。 + -我们可以尝试用一个结构体来类比简化的X86结构: +为了简单我们省略了位运算指令,很多高级指令。完整的X86指令在 https://github.com/golang/arch/blob/master/x86/x86.csv 文件定义。同时Go汇编还正对一些指令定义了别名,具体可以参考这里 https://golang.org/src/cmd/internal/obj/x86/anames.go 。 -```go -type X86CPU struct { - Flag uint - AX, BX, CX, DX int - SP uintptr - IP uintptr - Memery [...]int -} -``` - -其中Flag对应计算机的内部状态标志,我们一般不会直接读取Flag,只有在一些特殊的场景才会需要直接操作(比如操作系统中进程切换时保存和恢复状态)。 - -TODO - -