汇编学习
最后更新于:2022年6月25日 下午
学习的时候可以多想想汇编器应该怎么做,实验数据放在 github
首先下载或购买 王爽 《汇编语言》第三版,写的是真的好!下载 masm5 然后安装 DOSBox,安装好之后, 需要配置一下即可完美安装实验环境
Windows 在对应的目录下有 .bat
,Mac 在 ~/Library/Preference
中有 DOSBox Preference 在最下面添加
1 |
|
打开 DOSBox 然后输入 debug 就可以做书中的实验,确实好玩,跟着书一步一个脚印的学习即可
经典观点
- 学习汇编的目的:充分获得底层变成的体验,深刻理解机器运行程序的机理
- 知识屏蔽:不要一股脑的把所有概念都介绍,能少介绍少介绍,慢慢的露出全貌,就跟走台阶一下,每一步很小,走着走着就到底了很高的层次
- 汇编语言:汇编指令(和机器指令一一对应),伪指令(汇编器要处理的指令),其它符号(例如
+, -, *, /
) - 指令和数据在 CPU 看来没有任何区别,只是我们按照逻辑来定义的,完全可以复用代码作为数据,只是不推荐这么做,在当年红白机
.nes
只有几 kb 就能有各种画面各种可玩性,属实离谱,很大程序上时因为它们做了这样的复用,这是历史原因导致的,当时的存储空间十分有限。 - PC 的总线分成:地址总线,数据总线,控制总线
- PC 的总线也叫 CPU 的外部总线,CPU 内部也有总线连接着 运算器,寄存器,控制器,即可以理解 CPU 本身也是个 "PC"
- 内存地址空间:有效内存取决于地址总线,但是要主要以它不仅仅有 RAM,还有显存,各种 BIOS 的 ROM
- RAM 是可读可写的,ROM 是可读不可写的,但是我们平时的磁盘(SSD,机械硬盘)不是可读可写吗?为什么还叫 ROM,原来 ROM 分很多种,我们日常见到的一般叫闪存(Flash memory):详见 wiki
- xx 位 CPU 是指:运算器可以处理 xx 位的数据,寄存器宽度 xx 位,寄存器和运算器之间的通路是 xx 位。即它是 CPU 内部的概念与 PC 的总线无关
- 寄存器:不同的 CPU 寄存器个数和宽度各异,以下涉及寄存器都指 8086 的寄存器。一般 AX,BX,CX,DX 为通用寄存器,AX 又划分为 AH 和 AL,在写汇编指令时,
- 在众多 86 PC 中 8086 和 80386 是最重要的两个。其中 8086 汇编是 16 位的,它有 20 条地址总线,16 条数据总线;80386 是 32 位的,地址总线数据总线都是 32 条。
- 80386 有三种工作模式:实模式、保护模式、虚拟 86 模式。实模式为 DOS 系统的常用模式,直接内存访问空间被限制在1M字节;保护模式下 80386-DX 可以直接访问 4G 字节的内存,并具有异常处理机制;虚拟 86 模式可以同时模拟多个 8086 处理器来加强多任务处理能力。
- CPU 的数据总线和地址总线不一致时,可以用段地址和偏移地址来寻址,此时一般有一个寄存器来保存段地址(称为地址段寄存器:DS),不同的段地址和偏移地址可能形成同一物理地址。注意到内存并没有分段,段的划分来自 CPU,在 8086 中我们可以把起始地址是 16 倍数的一组连续内存单元定义为一个段。
- 段寄存器:CS(代码段), DS(地址段), SS(栈段), ES(额外段)。根据名字就可以知道,S 代表 Segment,C(code), D(data), S(stack), E(extra)
- CS:IP(Instruction Pointer)只是 CPU 当前要读取指令的地址。注意读取一条指令后,IP 自动增加(溢出不会改变 CS),以使 CPU 可以读取下一条指令(每一条指令的长度是不确定的),无法用
mov, add, sub
指令设置CS:IP
的值,最简单的方法用 jmp 指令 - CPU 把 CS:IP 指向的内存单元中的内容看作指令,把 DS:BP(Base Pointer) 指向的内存单元的内容看作数据,而它们可以有交集,所以说指令和数据时可以复用的。
- debug 功能属实好玩,常见命令:
R, D, E, U, T, A
,注意 R 指令可用于修改寄存器的值,显存中的值会一直变化,ROM 的值无法修改。 - 16 位寄存器来存储一个字,高 8 位存放高位字节,低 8 位存放低位字节(这种叫小端,反之叫大端:),一个字占两个字节,起始地址位 N 的字单元简称 N 字单元
- DS:BP 中 BP 使用的较少,注意到 DS 只能由其它寄存器的值来修改,
mov ax, [0]
等价于把DS:BP
中的内容写入 ax - 栈是相当重要的结构:任何时刻 SS:SP 指向栈顶元素,在 CPU 看来也并没有满和空的概念,有 push 和 pop 指令,执行 push 后 SP 自减 2,执行完 pop 后 SP 自加 2。示例,如果把
10000H~1000FH
当作栈空间,初始状态栈为空,此时SS:SP = 1000H:0010
。最大栈空间为 \(2^16\) - Debug 的 T 命令在执行修改寄存器 SS 的指令时,下一条指令也紧接着被执行。
- Debug 模式下编程和用汇编程序编程不一样。例如:Debug 默认十六进制,而汇编程序中默认 十进制(所以汇编程序中数据不能以字母开头),Debug 中
mov ax, [0]
等价于 汇编程序中mov ax, cs:[0]
,汇编程序中mov ax, [0]
等价于 Debug 中mov ax, 0
。 - 虽然汇编程序可以把代码段,数据段,栈段全部放在代码段运行,但是把它们分开是更优的选择
经典名言
这里再次强调一下,我们学习汇编的主要目的,就是通过用汇编语言进行编程而深入地理解计算机底层的基本工作机理,达到可以随心所欲地控制计算机的目的。基于这种考虑,我们的编程活动,大都是直接对硬件进行的。我们希望直接对硬件编程,却不希望用机器码编程。我们用汇编语言编程,就要用到编辑器(Edit)、汇编器(masm)、连接器(link)、调试工具(Debug) 等所有工具,而这些工具都是在操作系统上运行的程序,所以我们的学习过程必须在操作系统的环境中进行。我们在一个操作系统环境中,使用了很多工具,这势必要牵扯到操作系统、编译原理等方面的知识和原理。我们知识利用这些环境、工具来方便我们的学习,而不希望这些东西分散了我们的注意力。所以,对于涉及而不再我们的学习的主要内容之中的东西,我们只做简单的解释。
大小端现阶段状况
以下取自百度百科
Intel的80x86系列芯片是唯一还在坚持使用小端的芯片,ARM芯片默认采用小端,但可以切换为大端;而MIPS等芯片要么采用全部大端的方式储存,要么提供选项支持大端——可以在大小端之间切换。另外,对于大小端的处理也和编译器的实现有关,在C语言中,默认是小端(但在一些对于单片机的实现中却是基于大端,比如Keil 51C),Java是平台无关的,默认是大端。在网络上传输数据普遍采用的都是大端。