汇编 王爽
2022.5.28
VSCode配置
一 基础知识
1.14 各类存储器芯片
- PC机(微机)中存有多个存储芯片,分为RAM(带电存储,内存,Cache)和ROM(磁盘)
- BIOS是ROM的软件系统,通过它利用硬件设备进行最基本的输入输出
- 接口卡上RAM,比如显示屏的显存
1.15 内存地址空间
- CPU把物理上独立的器件的物理存储器看作一个由若干存储单元组成的逻辑存储器,每个物理存储器在这个逻辑存储器中占有一个地址
二 寄存器
2.1 通用寄存器
- 16位通用寄存器,用来存放一般数据
- 16位的AX分为AH和AL
- BX分为BH和BL
- CX分为CH和CL
- DX分为DH和DL
2.2 字在寄存器的存储
- 十六进制数之后加上H
2.3 几条汇编指令
- 高级语言描述汇编Intel汇编,与AT&T操作数方向相反
- 对于8位寄存器al的溢出,并不会在ah中产生进位
- 指令的两个操作数对象的位数应该是一致的
2.6 8086CPU给出物理地址的方法
- 物理地址=段地址*16+偏移地址,20位物理地址,由两个16位地址合成。可以寻址1MB的地址空间
2.7“段地址*16+偏移地址=物理地址”的本质含义
- 本质上,这种基础地质+偏移地址=物理地址的寻址模式是为了弥补寄存器位数不足造成的寻址能力不足的窘境
2.8 段的概念
- 可以根据需要,将地址连续,起始地址为16的配属的一组内存单元定义为一个段
2.9 段寄存器
- 8086CPU访问内存时由CS,DS,SS,ES提供段地址
2.10 CS和IP
- CS和IP指示了CPU当前要读取指令的地址
- 数据总线有多少位?8086CPU的地址总线有16位,以及怎么判断传输多少个字节?应该是通过控制总线,或者说指令可以通过开始字节判断其指令长度
- 在8086PC机启动执行的第一条指令是FFFF0H单元中的指令
2.11 修改CS,IP指令
- 8080CPU中大部分寄存器都可以用mov指令要改变,mov指令被称为传送指令。
- 只能通过转移指令来修改CS,IP的内容
- jump 段地址:偏移地址
- jump 某一合法寄存器:用寄存器修改IP
2.12 代码段
- 对于8086PC机,可以根据需要将一组内存单元定义为一个段。并且在执行的时候,将CS:IP指向起始地址。
实验 一
debug程序
- 打开emulator就可以进入Debug了注意jsdos版本
- 实验中需要用到的指令
- b800:0是8086pc显存的位置
三 寄存器
3.2 DS和[address]
- 可以通过ds段寄存器,与[n]中的数值来将内存单元的数据送入寄存器中,
- 不能用mov指令将数据传入段寄存器ds,只能通过寄存器传递值到段寄存器中
3.4 字的传送
- 对于mov,add, sub可以执行mov 寄存器,段寄存器,mov 内存单元,段寄存器,mov 段寄存器,内存单元
3.5 数据段
- 可以通过设置ds的值,将特定位置的二进制数据当作数据
3.7 CPU提供的栈机制
- 段寄存器SS和偏移寄存器SP,用来存储栈顶。push先减小栈顶,在放入元素SS用来指向栈的最底部,SS:SP用来指向当前栈顶
- pop是先弹出元素,再增大栈顶
3.8 栈顶超界问题
- 栈顶越界很危险,因为栈空间之外的地址空间存放了其他数据。但是8086CPU不会对栈顶溢出做保护处理
3.9 push, pop指令
- oush,pop处理的一定是字内存单元
- push,pop指令与mov指令区别在于其需要分为两步执行,而mov只需要执行一步。并且push,pop等栈操作,修改的只是SP,那么栈顶的变化范围只有0~FFFFH
3.10 栈段
- 当栈的空间为FFFFH,也就是64KB时,SP的初始值为0
实验 二
- 再执行D命令时,D命令也是一段程序,那么d 段地址:偏移地址中的段地址一般存放在DS段寄存器中
- 也可以使用d 段寄存器 : 偏移地址这样的格式E,A,U这些带有内存单元的地址命令也可以使用段寄存器
- 由于中断机制,在涉及到修改SS栈段寄存器的时候,都会自动执行下一条指令
- 为什么栈中的内容会发生变化呢?
四 第一个程序
jsdos MASM-v6.11中的asm文件
- 点击run
- 会执行你写的程序
- 你写的asm在jsdos中被命名为test可以发现是一致的
4.2 源程序
- 除了汇编指令,还存在伪指令,伪指令由汇编器来执行,汇编器根据伪指令来进行相关的编译工作。
- 一个汇编程序由多个段组成,一个汇编程序至少需要一个代码段
- end伪指令用来标识一个汇编程序的结束
- assume伪指令可以用来指明某一个段寄存器与某一个用segment...ends定义的段相关联
- 汇编程序源文件经过汇编器生成可执行文件
- codesg segment中的codesg是标号,作为一个段的名称,这个名称最后将会被处理为一个段的段地址。
- mov ax,4c00H 和 int 21H可以实现程序返回。以及与结束相关的指令
4.4 编译
- asm文件到obj,lst和crt文件是将asm编译为目标文件的过程中产生的中间结果,可以要编译器不生成这个文件
4.5 连接
- 对于obj文件需要经过连接,得到exe文件(windows中)
- obj生成exe文件的中间文件有映像文件map,那么连接的作用是
- 对于源程序很大的情况,可以将其分为多个文件,之后将它们的obj文件连接到一起
- 对于一个文件中调用了某个库,需要将obj文件与库文件连接
- 即使是只有一个源文件而且该文件没有调用库函数,也需要使用链接器对obj文件处理
4.8 谁将可执行文件中的程序装载进入内存并使它运行
- dos中的shell,也就是command(命令解释器),根据文件名找到可执行文件,然后将这个可执行文件程序载入内存设置CS:IP指向程序的入口,之后command暂停,CPU运行程序,执行完成之后,回到command
4.9 程序执行过程的跟踪
- 用debug程序去执行程序,那么一开始cx中存的是程序的大小
- dos装载exe文件的过程,其中DOS利用PSP(program SEGEMENT PREFIX)来和被加载程序通信
- ds中存的是PSP开始段地址,而CS存的是程序的段地址
- 在jsdos MASM-v6.11中还是用t命令
实验三
- PSP的头两个字节是CD 20
五 [BX]和loop指令
- [bx]表示,偏移地址在bx中,段地址在ds中
- 用描述性符号()来表示一个寄存器或一个内存单元中的内容
- idata表示常量,mov ds,idata是非法指令,应该不能直接将立即数放入ds
5.2 Loop指令
- CX中的值用来影响loop指令的执行结果,通常(注意,是通常)用loop实现循环功能,cx中存放循环次数。
- 循环中的标号代表着一个地址
- cx和loop指令结合实现循环功能
5.3 在DeBug中跟踪loop指令实现循环程序
- 在汇编源程序中,数据不能以字母开头
- g命令可以一次性执行很多命令
- jos MASM不存在p命令,
5.4 Debug和汇编编译器masm对指令的不同处理
mov ax, [0]
表示将ds:0处的数据送入ax中,但是在汇编源程序中,它会被当作mov ax,0
5.5 loop和[bx]的联合应用
- 不能够将字节单元的数据存入16寄存器
- 在循环中使用变量(例如bx)来改变要访问的内存单元内容,
5.6 段前缀
- 可以在[..]前面加上段寄存器,默认是ds,被称为段前缀
5.7 一段安全的空间
- 在
0:0026
处存放着重要的系统数据。在纯dos(dos是操作系统)方式(实模式),可以直接用汇编去操作硬件,而保护模式中的操作系统(比如windows2000),无法用汇编去操作硬件。0:200~0:2ff是一个安全的256字节的合法空间
实验四
- 程序运行开始时,cx中存有的是程序的大小
- offset的解法
六 包含多个段的程序
- 程序取得所需空间的方法有两种,一是在加载程序的时候为程序分配,二是在程序执行的过程中向系统申请
- 如果需要在程序加载的时候取得所需的空间,需要在源程序中做出说明,通过在源程序定义段来进行内存空间的获取
- 两种使用段的方式
6.1 在代码段中使用数据
- 在源代码中定义段,系统会帮助分配安全的内存,dw表示define word
- 由于程序段一开始是数据,那么需要设置程序的开始执行入口,end伪指令可以指定程序入口
6.3 将数据,代码,栈放入不同的段
- 一个段的大小不能超过64KB,8086模式的限制
- 定义不同的段就是要有不同的段名
- 对于段地址的引用,段名就是标号。end伪指令说明了程序的入口,而具体的汇编指令比如mov决定了CS:IP,SS:SP,DS等寄存器的值
实验五
- (5)中要注意一个段最少16字节
assume cs:code
a segment
dw 1, 2, 3, 4, 5, 6, 7, 8
a ends
b segment
db 1, 2, 3, 4, 5, 6, 7, 8
b ends
c segment
db 0, 0, 0, 0, 0, 0, 0, 0
c ends
code segment
start:
mov cx, 8
mov bx, 0
mov ax, a
mov ds, ax
s:
mov dl, [bx]
add dl, [bx + 16]
mov [bx + 32];一个段最少16字节
inc bx
loop s
mov ax, 4c00h
int 21h
code ends
end start
七 更灵活的定位内存地址的方法
7.1 and和or指令
7.5 [bx+idata]
mov ax, [200+x]
与mov ax, 200[bx]
与mov ax,[bx].200
效果一致
7.7 SI和DI
- SI和DI是与BX类似的寄存器,但是不能够分成两个8位寄存器
7.8 [bx+si]和[bx+di]
mov ax, [bx][si]
与mov ax, [bx + si]
效果一致
7.9 [bx+si+idata]和[bx+di+idata]
7.10 不同寻址方式的灵活应用
- 对于多重循环,需要保存好cx中的值,比较好的方法是栈保存,因为寄存器可能不够用和普通内存单元难以记住,容易混乱
八 数据处理的两个基本问题
- 段寄存器和寄存器有
8.1 bx, si, di和bp
- 只有这四个寄存器可以出现在[..]中
- bx和si,bx和di,bp和si,bp和di可以组合
- 只要在[..]中使用bp,而没有显式的给出段地址,那么段地址就在ss中
8.2 机器指令处理的数据在什么地方
- 执行指令前一刻的数据可以在三个地方:cpu内部,内存,端口
8.3 汇编语言中数据位置的表达
- 存在三个概念来表达数据位置:
- 立即数(idata),
mov ax, 1
- 寄存器,
mov ax, bx
- 段地址(SA)和偏移地址(EA)
- 立即数(idata),
8.4 寻址方式
- 8086存在:直接寻址,寄存器间接寻址,寄存器相对寻址,基址变址寻址,相对基址变址寻址
8.5 指令要处理的数据有多长
- 8086有两种尺寸的数据
- 通过寄存器指明处理的数据尺寸,
mov ax, 1
- 没有寄存器,可以通过X ptr指明内存单元长度,X可以是word或byte,
mov word ptr ds:[0], 1
,mov byte ptr ds:[0], 1
- push,pop指令只进行字操作
- 通过寄存器指明处理的数据尺寸,
8.7 div指令
- 除数(除号后面的数),8位或者16位,在一个reg或内存单元中
- 被除数,如果除数8位,被除数就是16位,AX中存放;除数16位,被除数32位,DX存放高16位,AX存放低16位。
- 例子是
8.8 伪指令dd
- db表示define byte,dw表示define word,dd来定义dword(double word)
8.9 dup
- dup与db,dw,dd配合使用
实验 七
mov [..], [..]
这样的语句是错的
九 转移指令的原理
- 转移指令可以控制CPU执行内存中某处代码的指令,可以修改IP,或同时修改CS和IP的指令称为转移指令
- 转移行为分为:
- 只修改IP,段内转移,如jmp ax
- 短转移,修改范围为-128~127
- 近转移,修改范围为-32768~32767
- 同时修改CS和IP,段间转移
- 只修改IP,段内转移,如jmp ax
- 转移指令分为:
- 无条件转移指令(jmp)
- 条件转移指令
- 循环指令(loop)
- 过程
- 中断
9.1 操作符offset
- 相当于取得标号的偏移地址,
9.3 依据位移进行转移的jmp指令
- jmp short 标号,这种是段内短转移,跳转范围为-128~127
- CPU在执行jmp指令的时候并不需要转移的目的地址,都是
EB 03
。由于转跳的两条指令之间相差3,那么03就是两条指令之间的差值。差值是在编译时算出来的。
9.4 转移的目的地址在指令中的jmp指令
jmp far ptr 标号
,是段间转移,那么标号指明了段地址和偏移地址,CS和IP- 对于段间转移,汇编指令就不是差值,而是要跳转过去的地址的真实CS:IP
9.5 转移地址在寄存器中的jmp指令
jmp 16位reg
,这个就是段内转移
9.6 转移地址在内存中的jmp指令
jmp word ptr 内存单元地址(段内转移)
,一个字中存放的是目的的偏移地址jmp dword ptr 内存单元地址(段间转移)
,高地址存放转移的目的段地址,低地址存放的是转移的目的偏移地址
测试点 9.1
- code segment开始的地方的偏移地址一定是0,那么只需要只要ds:[bx+1]起始地址的两个字节为0就可以了
9.7 jcxz指令
- jcxz指令是短转移指令,根据cx是否为0做判断
9.8 loop指令
- loop指令也是短转移,
9.9 根据位移进行转换的意义
- 短转移,使得程序在内存的不同位置,都可以执行正确
9.10 编译器对转移位移超界的检测
- 对于短转移,超出-128~127会报错
实验八
- 对于短转移,存储的是的是偏移地址之间的差值,那么F6表示的是-10,所以标号s处的nop,nop会被转化为EBF6,最后执行
mov ax, 4c00h与int 21h
实验九
- 怎么显示其他页的内容
十 CALL和RET指令
10.1 ret和retf
- ret是用栈中的数据,修改IP内容,实现近转移,ret就是
pop IP
- retf用栈中的数据,修改CS和IP内容,实现远转移retf就是
pop IP pop CS
10.2 call指令
- call指令不能实现短转移?短转移的转移范围是-128~127
10.3 依据位移进行转移的call指令
- call 标号是
push IP和 jmp near ptr 标号
10.4 转移目的地址在指令中的call指令
- call far ptr 标号是
push CS和push IP和jmp far ptr 标号
检测点 10.5
- 由于ds与ss相等,那么这里就是将下一条语句的ip入栈,之后直接执行下一条语句
10.7 call和ret配合使用
- call指令与ret指令配合可以实现子程序机制
10.8 mul指令
- 8位乘法,一个数默认放在AL中,另一个放在8位寄存器或者内存字节单元中。结果放在AX中。
- 16位乘法,一个数默认放在AX中,另一个放在一个16位reg或内存字单元。结果高位放在DX中,低位放在AX中。
10.10 参数和结果传递的问题
- 用寄存器来传递参数与存储返回结果