前言
汇编语言学习笔记
准备工作
edit.com
:编辑器
masm.exe
:汇编程序
link.exe
:链接程序
debug.exe
:调试程序
DOSBox
挂载真实磁盘并跳转
c
:挂在后的虚拟盘符 <dir>
:真实环境中的目录路径,不能包含中文
将挂载和跳转加入到启动选项
- Windows上配置文件在软件根目录下(
C:\Program Files (x86)\DOSBox-0.74-3\
),双击启动选项批处理文件(DOSBox 0.74-3 Options.bat
)后,会自动使用记事本打开配置文件(C:\Users\xxx\AppData\Local\DOSBox\dosbox-0.74-3.conf
),在文件末尾追加配置
- MacOS上的配置文件路径:
~/Library/Preferences/DOSBox 0.74-3 Preferences
全屏
- Windows:
alt
+enter
- MacOS:
option
+return
edit.com的使用
启动edit
存盘
启动edit时编辑文件
- 如果问价存在,则直接编辑
- 如果文件不存在,则创建新文件开始编辑
<name>
:文件名
编辑汇编程序
注释
段定义
段关联
1 2 3 4
| 段名 segment assume 寄存器:段名
段名 ends
|
段地址传送
1 2 3 4 5 6 7
| 段名 segment assume 寄存器:段名 mov ax,data mov ds,ax 段名 ends
|
符号地址
- 在指令之前写标号,标号是用于指向该指令所存放的地址的符号,被称作符号地址
- 标号可以卸载指令正前方,也可以写在指令正上方
- 不是每一个指令前都需要写标号,只有在需要获取其内存地址时才写标号,程序的第一个指令前一定要有标号
- 代码执行之前需要书写开始标号,执行结束需要书写结束标号,开始标号通常使用
start
命名
1 2 3 4 5 6 7 8 9 10 11 12
| 段名 segment assume 寄存器名:段名 标号1: 指令 目的操作数,源操作数 标号2:指令 目的操作数,源操作数 段名 ends
end 标号1 end 标号2
|
指令
- 伪指令:除了具体操作数的指令
- 指令:具体操作数的指令
代码段
1 2 3 4 5 6 7 8 9 10
| code segment assume cs:code start: ... code ends
end start
|
数据段
- 数据段需要在代码段中定义段关联和段地址传送
- 当需要存储数据时,可以定义数据段
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| data segment ... data ends
code segment assume cs:code,ds:data start: mov ax,data mov ds,ax ... code ends
end start
|
定义数据
- 数据定义伪指令
- 字节数据定义伪指令
- 字节数据伪指令使用
db
关键字,每次开辟1个字节的空间
- 字数据定义伪指令
- 字数据定义伪指令使用
dw
关键字,每次开辟2个字节的空间
- 双字数据定义定义伪指令
- 字数据定义伪指令使用
dd
关键字,每次开辟4个字节的空间
- 多字节数据存储原则
开辟空间并存入数据
1 2 3
| db 0,0,0 dw 0,0,0 dd 0,0,0
|
存入字符数据
只开辟空间不存入数据
1 2 3
| db ?,?,? dw ?,?,? dd ?,?,?
|
利用重复伪指令开辟更多空间
1 2 3
| db 重复次数 dup(?) dw 重复次数 dup(?) dd 重复次数 dup(?)
|
获取数据
- 定义数据前加上符号地址,通过符号地址即可获取数据,符号地址可以自定义名称
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| data segment 符号地址 db 重复次数 dup(?) 符号地址 dw 重复次数 dup(?) 符号地址 dd 重复次数 dup(?) data ends
code segment assume cs:code,ds:data start: mov ax,data mov ds,ax mov ax,符号地址 code ends
end start
|
编译的过程
汇编
<file>
:.asm汇编源文件,.asm
可省略
1 2 3 4 5
| masm <file>
Object filename: Source listing: Cross-reference:
|
Object filename:
:询问生成的.obj文件名,如果直接回车不指定,则使用默认文件名 Source listing:
:询问生成的列表文件名,如果直接回车不指定,则不会生成列表文件 Cross-reference:
:询问生成的调试文件名,如果直接回车不指定,则不会生成调试文件
链接
<file>
:.obj目标文件,.obj
可省略
1 2 3 4 5
| link <file>
Run File: List File: Libraries:
|
Run File:
:询问生成的.exe文件名,如果直接回车不指定,则使用默认文件名
执行
<file>
:.exe可执行文件,.exe
可省略
debug.exe的使用
使用debug将可执行程序载入内存
<file>
:.exe可执行文件
debug的命令
退出
查看寄存器的值
得到的值
- 得到的除了所有寄存器的值,还有CPU下一条将要执行的指令
1
| 段地址:偏移地址 代码的机器语言代码对应 指令 目的操作数,源操作数
|
从当前代码段开始向下反汇编
执行一条语句
执行到断点地址
查看内存空间
当前数据段
查看指定内存地址
寻址方式
直接寻址
寄存器寻址
间接寻址(寄存器间接寻址)
立即数寻址
汇编指令
- 汇编指令实质上就是机器语言的助记符,与机器语言没有本质区别,只不过程序员编程不需要记住复杂的二进制代码
- 汇编指令不区分大小写
存数
通过符号地址
通过符号地址+偏移量
通过真实地址
源操作数真实地址
:可以是真实地址值,也可以是寄存器中存储的内存真实地址
1 2 3
| mov 目的操作数,[源操作数真实地址]
mov 目的操作数,[DS:0000]
|
重复伪指令
加法指令
- 把源操作数与目的操作数相加,得到的结果放到目的操作数中
- 源操作数可以是立即数,目的操作数不可以是立即数
- 目的操作数是寄存器的时候,最好给寄存器初始化(
mov 寄存器,0
)
- 不可以在两个存储器间直接操作,必须借助寄存器,将立即数存到内存除外
减法指令
- 目的操作数减源操作数,得到的结果不放到目的操作数中,结果只用作影响寄存器标识位
将地址存到寄存器
1 2 3 4
| lea 可以存放地址的寄存器,符号地址
;获取地址所指向的数 mov 寄存器,[可以存放地址的寄存器]
|
中断
常见的DOS中断
21H
:DOS功能中断
返回DOS
- 在Windows平台下,写完8086汇编代码后,需要返回DOS,如果不写返回DOS的代码,DOS将中止
- 仿真环境下不需要返回DOS
DOS键盘输入
- 获取键盘输入的一个字符,将ASCII码值存到AL寄存器中
- ASCII码转数字:
-30H
,数字转ASCII码:+30H
带回显的输入
不带回显的输入
DOS屏幕输出
按任意键退出程序
- 无阻塞中断,不会打断程序运行
- 如果按下任意键,al的值为FF,如果没按下,al的值为00
将数据保存在系统堆栈段
压栈
弹栈
交换数据
1 2 3 4
| push 操作数1 push 操作数2 pop 操作数1 pop 操作数2
|
自增自减
自增1
自减1
逻辑运算指令
逻辑移位指令
循环左移
- 最高位移到最低位,其他各位左移一位
- 先将移动次数存储在cl寄存器,再根据次数向左移
1 2
| mov cl,移动次数 rol 目的操作数,存储次数的寄存器
|
循环右移
- 最低位移到最高位,其他各位右移一位
- 先将移动次数存储在cl寄存器,再根据次数向右移
1 2
| mov cl,移动次数 ror 目的操作数,存储次数的寄存器
|
控制转移指令
设置跳转点
无条件跳转
条件跳转(jump)
- 通过判断标识位,实现比较数值,然后跳转
z
:等于判断,通常用于有符号数,也可用于无符号数
g
:有符号数的大于判断
l
:有符号数的小于判断
e
:等于判断,通常用于无符号数,也可用于有符号数
a
:有符号数的大于判断
b
:有符号数的小于判断
n
:非判断
有符号数的判断
有符号数 > jz
:等于 > jg
:大于 > jl
:小于 > jnz
:不等于 > jng
:不大于 > jnl
:不小于 > jgz
:大于等于 > jlz
:小于等于 > jngz
:不大于等于、小于等于 > jnlz
:不小于等于、大于等于
无符号数 > je
:等于 > ja
:大于 > jb
:小于 > jne
:不等于 > jna
:不大于 > jnb
:不小于 > jaz
:大于等于 > jbz
:小于等于 > jnaz
:不大于等于、小于等于 > jnbz
:不小于等于、大于等于
举例:判断相等
- cmp在这里的目的只是用于改变寄存器标识位
- 判断标识位中
1 2
| cmp 操作数1,操作数2 jz 跳转点符号地址
|
条件跳转(loop)
- 在进入循环之前,在
cx
寄存器设置一个循环初值(只能用cx
寄存器存循环初值),每次循环cx自减1,直到cx寄存器值为0时,跳出循环
1 2
| mov cx,源操作数 loop 跳转点符号地址
|
loop循环与dec+jnz循环
按位逻辑运算
原理
任意数,和0相与,都变为0 任意数,和1相与,数字不变
任意数,和1相或,都变为1 任意数,和0相或,数字不变
按位相与
按位相或
按位相非
按位相异或
接口编程
向接口芯片写入B口数据
1 2 3
| mov al,数据 mov dx,接口地址 out dx,al
|
从接口芯片A口读取数据
无限循环
通过符号地址
简写
软件方式实现减速
- 利用空循环实现减速
- 如果减速速度不够,可以使用多层嵌套循环
- 这种方法只能实现不精确的减速
1 2 3 4 5
| mov ax,100 a: dcc ax cmp ax,0 jnz a
|
子程序
- 封装常用的代码块,如果需要反复使用相同的代码,可以直接调用子程序
定义子程序
- 子程序定义需要在代码段内定义,可以放在代码段内的任意位置,通常放在代码段末尾(返回DOS之后)
属性
> far
:远属性,允许跨段调用 > near
:近属性,只允许在相同段内调用
1 2 3 4 5 6
| 子程序名 proc 属性
... ret 子程序名 endp
|
调用子程序
完成