汇编基础知识
编译器
可以将汇编指令转换成机器指令的翻译程序
程序员 –> 汇编指令 –> 编译器 –> 机器码 –> 计算机
指令和数据
指令和数据其实是应用上的概念。在内存或磁盘上,指令和数据没有任何的区别,都是以二进制信息的形式存储
- 例如,内存中的二进制信息:1000100111011000
- 可以看成大小为
89D8h
的数据来处理 - 可以看成是指令
mov ax, bx
来执行
- 可以看成大小为
存储单元
存储器被划分为若干个单元,每个存储单元从 0 开始顺序编号
- 微型机存储器的容量以 字节 (Byte) 作为最小单位
- 1 个字节 (Byte) = 8 个比特 (bit)
- 例如,一个存储器有 128 个存储单元,则它可以存储 128 个字节 (Byte)
1 KB = 1024 B
1 MB = 1204 KB
1 GB = 1024 MB
1 TB = 1024 GB
CPU 对数据的读写
CPU 想要进行数据读写,需要三类信息:
- 存储单元的地址(地址信息) —— 地址总线
- 器件的选择、读或写的命令(控制信息) —— 控制总线
- 读或写的数据(数据信息) —— 数据总线
- 示例:CPU 从 3 号内存单元中读取数据的过程
地址总线
CPU 是通过地址总线来指定内存单元的
地址总线上能传送多少个不同的信息,CPU 就能对多少个内存单元进行寻址
若一个 CPU 有 N 根地址线,则可以说这个 CPU 的地址总线宽度为 N,最多可以寻址 $2 ^ {N}$ 个内存单元
示例:地址总线为 10 根的 CPU 向内存发出地址信息 11
数据总线
实现 CPU 与内存或其他器件之间的数据传送
数据总线的宽度决定了 CPU 和外界的数据传送速度
8 根数据总线一次可以传输 8bit 二进制数据(一个字节)
示例:当 CPU 向内存写入数据
89DF8h
时
8086CPU 地址总线为 16 位,可以一次传输 16bit 数据,因此
89D8h
可以一次传输完成如果是
8088CPU
的话,地址总线只有 8 位,一次就只能传输 8bit,因此需要传输两次,先传送D8h
,再传送89h
控制总线
实现 CPU 对外部器件的控制
有多少根控制总线,就意味着 CPU 提供了多少种对外部器件的控制
搭建 DOSBox debug 环境
下载 DOSBox:Download DOSBox0.74-3-win32-installer.exe (DOSBox) (sourceforge.net)
双击安装
在 DOSBox 的安装目录下双击 DOSBox 0.74-3 Options.bat
文件,弹出 dosbox-0.74-3.conf
文件
在 dosbox-0.74-3.conf
的最后,加上下面两句 (尽量不要加中文注释,否则打开 DOSBox 会报错,无法进入到指定文件夹):
mount c d:\masm
c:
这两句的意思是:
将本机的 d:\masm
作为 DOSBox 环境的 C 盘,并进入到 DOSBox 的 C 盘目录下 (作为 DOSBox 环境 C 盘的 d:\masm
目录可自己设定,尽量不要有中文)
保存后,打开 DOSBox,显示本地目录被成功挂载为 C 盘
另外,DOSBox 默认的运行窗口特别小,字体也很小
可以通过 dosbox-0.74-3.conf
自定义窗口尺寸,找到如下位置: (大概在 29 行)
windowresolution=original
output=surface
修改为:
windowresolution=1024x768
output=opengl
重新打开 DOSBox:
然后还需要下载汇编、链接程序
下载地址:DOSBox 编译链接相关程序
主要是 MASM.EXE
、LINK.EXE
、DEBUG.EXE
(ML.EXE
为 MASM.EXE
+ LINK.EXE
)
MASM.EXE
:汇编程序,用于汇编源程序 .asm
,得到目标程序 .obj
LINK.EXE
:链接程序,用于链接目标程序,得到可执行程序 .exe
DEBUG.EXE
:调试程序,用于调试可执行程序
搭建 VScode debug 环境
VScode 提供了 MASM 和 DOSBox 环境的插件,并且 VScode 本身也是一款十分出色的编辑器,提供了各种语法高亮,可以很方便的搭建 debug 环境
打开 VScode 中的扩展栏,搜索 masm,找到 MASM/TASM
这个插件,安装
这个插件会将 DOSBox 以及汇编编译器 MASM 都安装好,也不需要我们再去挂载之类的操作了
写完代码后,直接右键运行或者调试
常用的 debug 指令
参数 | 意义 |
---|---|
r | 查看、更改 cpu 寄存器内容 |
d | 查看内存中内容 |
e | 改写内存中内容 |
u | 将内存中机器指令翻译成汇编指令 |
a | 以汇编格式在内存中写入一条指令 |
t | 单步执行,一次只执行一条机器指令 |
p | 类似 t 命令,不过遇到子程序调用的时候不会进入子程序逐条执行,而是直接执行完子程序代码。另外,在遇到 loop 循环指令时,会直接执行到 CX=0 |
g | 该命令后面可以跟地址和断点,格式为:g [=address] [breakpoints] ,运行到内存指定位置的代码后暂停,如果不加参数默认是从当前 IP 运行到程序结束。可以用来跳过 loop 循环 |
q | 退出 debug |
r 命令
- 输入
r
后回车,会显示所有寄存器的数值
- 输入
r ax
后回车,会显示当前ax
中的数据,在':'
后面输入内容后回车,即可修改ax
的数据(十六进制)
d 命令
- 输入
d
后回车,显示内存数据,区域地址为上次查看的位置继续往后的内存数据
- 输入
d 1000:00
后回车,显示段地址为 1000,偏移地址为 0 的内存数据
- 输入
d 1000:00 ff
后回车,显示段地址为 1000,偏移地址为 0 到 ff 的内存数据
e 命令
- 输入
e 1000:00 23 11 22 66
后回车,可以一次性修改段地址为 1000,偏移地址为 0 的 4 个数据的值为 23、11、22、66(数据的个数自己可以随意选择)
- 输入
e 1000:00
后回车,可以根据提示按地址顺序一个一个修改数据的值。'.'
前面的数表示原来的数据,在'.'
后面输入修改后的数据,然后按空格继续修改下一个地址的数据,最后按回车结束修改,例如修改为 1、2、3、4
u 命令
- 输入
u
后回车,可以将上次查看的区域地址位置继续往后的内存地址上的机器码翻译为汇编指令,并显示出来
- 输入
u 1000:00
后回车,可以将段地址为 1000,偏移地址为 0 的内存地址上的机器码翻译为汇编指令,并显示出来
a 命令
- 输入
a 1000:00
后回车,可以向段地址为 1000,偏移地址为 0 的内存地址上输入汇编指令,输入完成后,继续回车结束
t 命令
- 输入
t
后回车,即可从CS:IP
所指向的地址取出一条指令单步执行(需要提前指定 CS 和 IP 的值)
p 命令
- 对于存在 loop 循环的程序,先单步执行到 loop 指令,使用
p
命令可以直接执行到 CX = 0 跳出循环
g 命令
- 该命令后面可以跟地址和断点,运行到内存指定位置的代码,也可以用
g
命令来跳过 loop 循环
debug 中的标志寄存器
在 DEBUG 中,标志寄存器是按照有意义的各个标志位单独表示的
- 在 DEBUG 中对已知标志位的表示:
标志 | 值为 1 的标记 | 值为 0 的标记 |
---|---|---|
OF | OV | NV |
DF | DN | UP |
SF | NG | PL |
ZF | ZR | NZ |
PF | PE | PO |
CF | CY | NC |