汇编语言学习笔记
汇编语言
主要知识点来自《汇编语言》速成指南(全程敲代码),配套材料:
- 王爽老师的《汇编语言》
- 使用DOSbox模拟运行8086CPU汇编语言
1. 入门
简单引入关于8086CPU的知识。
CPU内部主要由运算器、控制器、寄存器三大部分组成[1]。
运算器: 负责算术运算(+ - * / 基本运算和附加运算)和逻辑运算(包括 移位、逻辑测试或比较两个值等)。
控制器: 负责应对所有的信息情况,调度运算器把计算做好。
寄存器: 它们可用来暂存指令、数据和地址。既要对接控制器的命令,传达命令给运算器;还要帮运算器记录处理完或者将要处理的数据。
8086 CPU 中寄存器总共为 14 个,且均为 16 位[2]。它们分为:
通用寄存器
-
AX,BX,CX,DX称作为数据寄存器:
- AX (Accumulator):累加寄存器,也称之为累加器;
- BX (Base):基地址寄存器;
- CX (Count):计数器寄存器;
- DX (Data):数据寄存器;
-
SP和BP又称作为指针寄存器:
- SP (Stack Pointer):堆栈指针寄存器;
- BP (Base Pointer):基指针寄存器;
-
SI和DI又称作为变址寄存器:
- SI (Source Index):源变址寄存器;
- DI (Destination Index):目的变址寄存器;
控制寄存器:
- IP (Instruction Pointer):指令指针寄存器;
- FLAG:标志寄存器;
段寄存器:
- CS (Code Segment):代码段寄存器;
- DS (Data Segment):数据段寄存器;
- SS (Stack Segment):堆栈段寄存器;
- ES (Extra Segment):附加段寄存器;
其中,通用寄存器和段寄存器是我们最常用的寄存器。8086CPU可以使用H和L来表示寄存器的高8位和低8位,如AX寄存器的高8位为AH,低8位为AL。但是这只针对于AX、BX、CX、DX这四个寄存器,其他的寄存器没有这样的表示方法。
挂载DOSBox的C盘到本地的D:\masm
目录
mount c d:\masm\
切换到C盘
c:
查看文件
dir
1.1 使用Debug
debug
R D E U T A
- R: 显示或更改寄存器的内容
- D: 显示内存内容
- E: 修改内存内容
- U: 汇编指令,将机器码转换为汇编指令
- T: 单步执行
- A: 以汇编指令的格式显示内存内容
记忆方法:ture ad
R: read
D: display
E: edit
U: unassemble
T: trace
A: assemble
R 表示显示或更改寄存器的内容
显示:
R
更改
R [寄存器名]
D 表示显示内存内容
D
D指令默认显示内存中的128个字节,从0000开始,每行显示16个字节,每个字节用两个十六进制数表示,每行的最后显示这16个字节对应的ASCII码。
D [地址]
D [地址] [长度]
E 表示修改内存内容,修改内存中的数据
E [地址] [数据]
连续修改内存中的数据,从某个地址开始,修改多个数据
E [地址]
A 表示以汇编指令的格式在内存中写入指令
A [地址]
T 表示单步执行(搭配A使用)
T
U 表示将机器码转换为汇编指令
U [地址]
1.2 常用指令
mov add sub
mov
: move,移动,赋值
mov ax, 1
表示将1赋值给ax寄存器,然后ax寄存器的值为1
mov ax, bx
表示将bx寄存器的值赋值给ax寄存器
add
: 加法
add ax, 1
表示将ax寄存器的值加1,然后将结果赋值给ax寄存器
add ax, bx
表示将ax寄存器的值加bx寄存器的值,然后将结果赋值给ax寄存器
sub
: 减法
sub ax, 1
表示将ax寄存器的值减1,然后将结果赋值给ax寄存器
sub ax, bx
表示将ax寄存器的值减bx寄存器的值,然后将结果赋值给ax寄存器
mul div and or乘除指令及所有寄存器英文名
mul
: 乘法
mul [源操作数]
mul
指令进行乘法运算有两种形式:
若操作数为8位,则乘数在AL中,被乘数在指定的源操作数中,乘积在AX中。
若操作数为16位,则乘数在AX中,被乘数在指定的源操作数中,乘积在DX:AX
中。
mul [源操作数]
div
: 除法
div [源操作数]
div
指令进行除法运算有两种形式:
除数为8位,被除数为16位,默认在AX中存放。结果的商在AL中,余数在AH中。
除数为16位,被除数为32位,默认在DX和AX中存放,DX存放高16位,AX存放低16位。结果的商在AX中,余数在DX中。
and
: 与运算
and [目的操作数], [源操作数]
or
:或运算
or [目的操作数], [源操作数]
shl shr inc dec xchg neg指令,中断int指令
shl
: 逻辑左移
shl [目的操作数], [移动位数]
表示将目的操作数的值左移移动位数位,然后将结果赋值给目的操作数。
逻辑左移:左移后,右边补0
shr
: 逻辑右移
shr [目的操作数], [移动位数]
表示将目的操作数的值右移移动位数位,然后将结果赋值给目的操作数。
逻辑右移:右移后,左边补0
除了逻辑左移和逻辑右移,还有算术左移和算术右移,算术左移和算术右移的区别在于,算术左移和算术右移在移动位数位的时候,左边补的是0,右边补的是符号位。
指令:sal
sar
此外,还有循环左移和循环右移,循环左移和循环右移在移动位数位的时候,左边或者右边补的是移动前的最后一位。
指令:rol
ror
inc
: 自增
inc [目的操作数]
表示将目的操作数的值加1,然后将结果赋值给目的操作数。
dec
: 自减
dec [目的操作数]
表示将目的操作数的值减1,然后将结果赋值给目的操作数。
xchg
: 交换
xchg [操作数1], [操作数2]
表示将操作数1和操作数2的值进行交换。
neg
: 取反
neg [目的操作数]
表示将目的操作数的值取反,然后将结果赋值给目的操作数。
int
: 中断
int [中断号]
表示执行中断号对应的中断程序。
比如:
int 0
表示执行中断号为0的中断程序。在使用div
指令的时候,如果除数为0,就会触发中断号为0的中断程序。
只要进入中断程序,就会跳转到中断程序的入口地址,然后执行中断程序的代码,执行完中断程序的代码后,再跳转回来继续执行中断指令后面的代码。
2 基础知识
ds寄存器,段地址,偏移地址,物理地址
一个物理地址可以通过段地址和偏移地址表示。且表示不是唯一的,比如:
物理地址 段地址 偏移地址
21F60H 2000H 1F60H
2100H 0F60H
21F0H 0060H
21F6H 0000H
1F00H 2F60H
DS:数据段寄存器
DS寄存器存放的是段地址,偏移地址存放在指令中。
r ds
然后将DS寄存器的值修改为段地址
mov ax, [偏移地址]
通过上述操作就可以将某一物理地址里的数据赋值给ax寄存器。
这里可能会涉及到大小端存储的问题
首先解释一下大小端存储:
大端存储:高位字节存放在低位地址,低位字节存放在高位地址
小端存储:高位字节存放在高位地址,低位字节存放在低位地址高位字节是指在一个字节中,数值高的那部分。低位字节是指在一个字节中,数值低的那部分。比如:
0x1234
,高位字节就是0x12
,低位字节就是0x34
。当我们在内存里从低地址向高地址扫描时,如果先扫描到的是高位字节,那么就是大端存储,如果先扫描到的是低位字节,那么就是小端存储。
8086CPU是小端存储,所以在使用mov
指令的时候,需要注意大小端存储的问题。
注意DS不能mov
赋值立即数,只能mov
赋值寄存器。
举例:
mov ax, 21F0
mov ds, ax
mov al, [0060]
表示将21F60H这个物理地址里的数据赋值给ax寄存器的低8位。
CS,IP寄存器
CS : 代码段寄存器;IP : 指令指针寄存器。在8086机中,任意时刻,CPU将CS:IP指向的内容当作指令来执行[3]。
CPU执行指令的过程[3:1]:
- CPU从CS:IP指向的地址读取指令,读取的指令进入到指令缓冲器中;
- IP = IP + 所读取的指令长度,从而指向下一条指令;
- 执行指令。转到步骤 1 ,重复这个过程。
由此我们可以看出,代码存储在内存中,CPU靠CS:IP来指向代码的位置,然后执行代码。代码也是数据,只不过是CPU执行的数据。所以代码也可以进行对应的运算,比如加法、减法等。
jmp指令
jmp
指令用于无条件跳转,跳转到指定的地址。
jmp
指令有两种形式:
jmp [目的操作数]
这种形式的jmp
指令,目的操作数是一个寄存器,表示将寄存器的值赋值给IP寄存器,然后CPU执行下一条指令的时候,就会从IP寄存器指向的地址开始执行。
jmp [段地址]:[偏移地址]
这种形式的jmp
指令,目的操作数是一个物理地址,段地址和偏移地址都是直接地址。表示将段地址赋值给CS寄存器,将偏移地址赋值给IP寄存器,然后CPU执行下一条指令的时候,就会从CS:IP指向的地址开始执行。
SS,SP 寄存器
SS: 堆栈段寄存器;SP: 堆栈指针寄存器。在8086机中,任意时刻,CPU将SS:SP指向的内容当作堆栈来使用。
SS:SP指向的内容是堆栈的栈顶,堆栈的栈顶是指堆栈中最后一个数据的地址。堆栈是一种特殊的数据结构,它是一种先进后出的数据结构。
入栈:
push [源操作数]
表示将源操作数的值入栈,然后将SP寄存器的值减2,然后将结果赋值给SP寄存器。
出栈:
pop [目的操作数]
表示将栈顶的值出栈,然后将SP寄存器的值加2,然后将结果赋值给SP寄存器。
需要注意的是,数据以字为单位入栈和出栈,在内存中的存储方式是小端存储,所以在入栈和出栈的时候,需要注意大小端存储的问题。
越界问题:
计算机对于出栈和入栈的操作,是不会检查栈的大小的,所以在使用栈的时候,需要注意栈的大小,防止越界。
参考与注释:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了