「BUAA X86 Programming」Experiment Notes
Part 0 前言
本文主要为笔记性质,主要参考资料来源包括:
- 熊桂喜老师课程资料(slides, 教材等)
- dhy学长的仓库
本课程主要学习的是最经典的8086
Part 1 8086结构与寄存器
通用寄存器:4个,16位,每个又可分为H
(high)和L
(low)各8位
AX
:16位,可分为2个8位AH
和AL
(累加器Add)BX
:16位,可分为2个8位BH
和BL
(基址寄存器Base)CX
:16位,可分为2个8位CH
和CL
(计数寄存器Count)DX
:16位,可分为2个8位DH
和DL
(数据寄存器Data)
段寄存器
CS
:Code Segment,代码段DS
:Data Segment,数据段SS
:Stack Segment,堆栈段ES
:Extra Segment,附加段
指针寄存器
SP
:Stack Pointer,堆栈指针BP
:Base Pointer,基址指针
变址寄存器
SI
:Source,源DI
:Destination,目的
指令指针寄存器
IP
:类似PC
特定组合
CS:IP
:当前可执行点(当前将执行的代码地址)(CS
指向代码段基地址,IP
指向当前指令相对基地址的偏移)SS:SP
:当前堆栈(SS
指向堆栈基地址,SP
是栈顶相对基地址的偏移)
Part 2 寻址方式与指令系统
指令格式
1 | op dst, src ; 由“源”至“目的” |
根据操作数(2个,1个,0个)的不同,可能有以下几种情况:
op op2, op1
op op1
op
寻址方式
与数据有关的寻址(6种)
- 立即寻址
- 寄存器寻址
- 直接寻址
- 寄存器间接寻址
- 寄存器相对寻址
- 基址变址寻址
Part 1 DOSBox环境
按照老师给的详细说明一步一步来即可配置好环境,没有什么坑点。
启动
- 打开DOSBox
mount c c:\
将主机目录c:\
映射到虚拟盘符c
c:
进入虚拟盘符c
cd masm
进入工作目录
退出
exit
工具简介
masm.exe
汇编器link.exe
链接器debug.exe
调试器edit.exe
编辑器
Part 2 EDIT
编辑器
快捷键
ctrl + f10
:捕捉/释放鼠标
Part 3 MASM
汇编器
Part 4 LINK
链接器
Part 5 DEBUG
调试器
调试tttt.exe
:
1 | debug tttt.exe |
调试命令:
d
:显示内存单元内容- 无参数默认从
CS:IP
开始,连续使用则地址接续向后移动 - 添加形如
xxxx:yyyy
的参数可以指定其实地址,如DS:0
,78a:0
(不要求一定写四位数)
- 无参数默认从
e
:修改指定内存单元的内容,每次修改一个字节- 须添加参数,形式如
d
命令,否则报错;如r ds:0
- 执行后会弹出形如
078a:0000 10.
,其中078a:0000
是即将修改的字节(说明我们的ds
的值就是078a
),10
是该字节中当前存放的值(十六进制),此时可以键入新值(十六进制) - 每次可以修改1个字节
- 须添加参数,形式如
r
:查看和修改寄存器- 查看:不加参数,可以查看所有寄存器信息和下一条指令的信息(
CS:IP
和指令内容) - 修改:指定寄存器名,如
AS
等
- 查看:不加参数,可以查看所有寄存器信息和下一条指令的信息(
u
:反汇编,查看机器码对应的汇编程序- 不指定/指定起始地址:类似
d
命令
- 不指定/指定起始地址:类似
a
:修改汇编指令t
:单步执行,类似step in
p
:执行某个子程序/循环等,类似step over
g
:执行到某个指令地址处,类似断点q
:退出调试器
Part 6 程序结构
三列格式
- 标号(符号、变量,不区分大小写):编程逻辑地址(立即数)
- 指令或伪指令(助记符)
- 操作数
伪指令
SEGMENT, ENDS, END
SEGMENT, ENDS
:定义段END
:定义总结束,标号为程序入口,被编译设置为初始的CS:IP
PROC, ENDP
PROC, ENDP
:定义子程序
$
当前位置计数器,记录当前偏移值
最常见的用途是计算长度,如
1
2TABLE DW 2,3,4 ...
TABLE_LEN EQU $-TABLE
操作符
FAR, NEAR
三段式:堆栈段、数据段、程序段
定义段
1 | [段名] SEGMENT [对齐类型][组合类型] [类别名] |
[段名]
:STACK
堆栈段DATA
数据段CODE
程序段
[对齐类型]
:定义段在内存中分配时起始边界设定PAGE
:本段从页的边界开始,一页是256字节PARA
:本段从节的边界开始,一节是16字节WORD
:本段从内存的偶字节地址(字对齐)开始,段间至多1字节BYTE
:本段从字节地址开始,段间无间隙
[组合类型]
:STACK
:说明该段为堆栈段的一部分,连接程序在连接时会把所有同名的具有STACK
组合类型的段连接成一个连续段,并将SS
初始化为这个连续段的首地址,用段内的最大偏移地址初始化SP
。正确地定义了段的STACK
属性后可以在主程序中省略对SS,SP
的初始化。
堆栈段
1 | STACK SEGMENT PARA STACK |
数据段
1 | DATA SEGMENT PARA |
程序段
1 | CODE SEGMENT |
定义过程(子程序)
1 | [过程名] PROC [NEAR|FAR] |
[过程名]
:自己定义NEAR
:RET
指令在汇编后仍为RET
,属于段内返回,从堆栈中弹出一个字作为IP
值FAR
:RET
指令在汇编后变成了RETF
,属于段间返回,从堆栈中先后弹出两个字作为IP
和CS
定义数据
1 | [变量名] [伪指令] [值] |
[伪指令]
:DB
:变量,define byte
,字节变量DW
:变量,define word
,字变量DD
:变量,define double word
,双字变量EQU
:常量,被编译为值,不占内存
[值]
- 常数
$
:当前位置计数器DUP
:重复操作符(用于定义数组或堆栈空间)?
:表达式,表示不预置任何内容- 字符串表达式
- 地址表达式
数组定义(DUP
的使用)
1 | ARRAY1 DB 2 DUP(0, 1, 0FFH, ?) ; 定义一个简单的二维数组 |
其中对ARRAY1
的定义和如下等价:
1 | ARRAY1 DB 0, 1, 0FFH, ?, 0, 1, 0FFH, ? |
对ARRAY2
的定义是一段长度为100字节的连续空间且没有初始化($
表示不预置,无初值)
嵌套DUP
,相当于多维数组:
1 | ARRAY3 DB 2DUP(1, 2DUP('A', 'B'), 0) |
'A', 'B'
:初值为字符(ASCII码),与41h, 42h
等价DUP
可以嵌套,相当于多维数组- 等价于
ARRAY3 DB 1, 'A', 'B', 'A', 'B', 0, 1, 'A', 'B', 'A', 'B',0
地址表达式
定义数据时可以使用DW, DD
伪指令将标号或变量的偏移地址或整个逻辑地址存入到另一个变量中。使用DD
来存储逻辑地址时,第一个字(低字)为偏移地址,第二个字(高字)为段地址。这里的参数实际上是一个地址表达式。
1 | X1 DW ? ;X1中的内容未预置 |
This is copyright.