汇编前四章学习笔记

汇编学习笔记(by 小白奋斗ing)

参考《汇编语言(第三版)》王爽著

1.基础知识

1.1 机器语言

二进制编码

1.2 汇编语言

image-20221130224318515

1.3 汇编语言的组成

1.4进制表示符

二进制(B),十六进制(H)

2.寄存器

2.1通用寄存器

8086CPU的所有寄存器都是16位的,可以存放两个字节。AX、BX、CX、DX这4个寄存器通常用来存放一般性的数据,被称为通用寄存器。

image-20221130225838476

8086CPU上一代CPU中的寄存器是8位的,为保证兼容,上述四个寄存器可以分为两个可独立使用的8位寄存器使用:

例如:AX可分为AH和AL

image-20221130230203600

8个高位构成AH,8个低位构成AL

例如:

image-20221130230345452

2.2字在寄存器中的存储

字节:byte,1 byte=8 bit,刚好存在8位寄存器中

字:word,1 word=2 byte,两个字节分别占用字的高位和低位,一个字刚好存在16位寄存器中

image-20221130230709562

2.3几条汇编指令

image-20221130230911795

例题运算:

image-20221130231043590

ax=ax+bx=8226H+8226H=1044CH,保留044CH

image-20221130231551914

al=C5H+93H=158H,保留58H,故ax=0058H

此处有规律,比如ax=2640H,则ah=26H,al=40H,在做运算时,可以将ax拆成ah

和al进行运算。

2.4物理地址

我们知道,CPU访问内存单元时,要给出内存单元的地址。所有的内存单元构成的存储空间是一个一维的线性空间,每一个内存单元在这个空间中都有唯一的地址,我们将这个唯一的地址称为物理地址。

内存单元的存储空间的线性空间地址就是物理地址

2.5 16位结构的CPU

image-20221130233040842

2.6 8086CPU给出物理地址的方法

image-20221130233413948

image-20221130233534526

补充:一个X进制的数据左移1位,相当于乘以X

2.7 “段地址x16+偏移地址=物理地址”的本质含义

CPU最大为16位,不能直接传出20位的数字信息,利用地址偏移将20位数字拆分为两个16位的数字信息进行传输

2.8 段的概念

CPU对内存进行分段处理(内存本身没有分段)

image-20221203181753025

2.9 段寄存器

段地址需要存放在8086CPU的段寄存器中。8086CPU有4个段寄存器:CS、DS、SS、ES。8086CPU访问内存是,从这四个段寄存器中获取内存单元段地址

CS 代码寄存器	CS:IP指定CPU处理的地址
DS 数据段寄存器	DS:[···]指定内存段的位置
SS 堆栈段寄存器	SS:SP指定栈顶的位置
ES 附加段寄存器

2.10CS和IP

CS为代码段寄存器,IP为指令指针寄存器。其中设CS中内容为M,IP中内容为N,8086CPU将从内存Mx16+N单元开始,读取一条指令执行。

image-20221203182515633

运行示例:

初始状态获取CS:2000H,IP:0000H

image-20221203183807248

CS、IP放入地址加法器运算2000Hx16+0000H=20000H

image-20221203184138487

数据传入输入输出控制电路、再传入地址总线

image-20221203184303497

CPU通过数据总线从内存20000H开始读取数据

image-20221205220043547

输入输出控制电路将机器指令送入指令缓冲器

image-20221205220229196

读取一条指令后,IP自动增加(当前读入B82301字节大小为3,故增加3字节)。也就是CPU控制内存从哪里开始读取

image-20221205220636407

执行控制器执行指令,则AX被赋值为0123。这里可以看出CPU并非在结果出现后才进行控制下一次运算的,而是进入指令缓冲器后就开始执行下一次。

image-20221205220956216

8086CPU工作简要概括为:

image-20221205221401090

8086CPU刚开始工作(或复位时),CS=FFFFH,IP=0000H,故FFFF0H单元中的指令是8086PC机开机执行的第一条指令

2.11 修改CS、IP的指令

mov被称为传送指令

可以修改CS、IP内容的指令被称为转移指令

jmp指令可以修改CS、IP的内容

格式jmp 段地址:偏移地址jmp CS:IP

image-20221205232546058

jmp ax含以上类似mov IP,ax,作用是只修改IP的值

image-20221205232917638

例题:

该题注意要点==jmp只改变IP的值==,这题最后会陷入循环中

image-20221205233857839

2.12 代码段

image-20221205234336804

代码段是人为定义的,想要让CUP执行这段代码,就必须指定CS=123BH,IP=0000H

实验debug

debug参数:
R查看、改变CPU寄存器的内容
D查看内存中的内容
E改写内存中的内容
U将机器指令翻译成汇编指令
T执行一条机器指令
A以汇编指令的格式在内存中写入一条机器指令

3.寄存器(内存访问)

3.1 内存中字的储存

CPU中用16位寄存器存储一个字,8个高位存放在高位字节,8个低位存放在低位字节。而在内存中存储,则是一个单元存放一个字节,一个字要用两个地址连续的内存单元来存放,低位字节放在低地址单元中,高位字节放在高地址单元中。

image-20221206092800813

在上图中,若存放一个字,由0、1两个字节单元组成,对于字单元来说,0号单元是低地址单元,1号单元是高地址单元。以此类推,起始地址相对后续地址单元为低地址值单元,后续单元以此类推

字单元:即存放一个字型数据(16位)的内存单元,有两个地址连续的内存单元组成。高地址内存单元中存放字型数据的高位字节,低地址内存单元中存放字型数据的低位字节。

例题:对于图3.1

image-20221206093747915

3.2 DS和[address]

image-20221206094439157

image-20221206102704323

ds 为内存单元的段地址
[···]表示一个内存单元,[0]表示内存单元偏移地址为0
mov al,[0] //8086CPU中将(ds:0-ds:1)的数据读到al中
mov [0],al	//将数据从寄存器送入内存单元,al的16进制值赋值给10000H的内存地址

ds属于段寄存器,在8086CPU中不能直接将数据送入ds,需要使用一个寄存器进行中转

3.3 字的传送

8086CPU是16位结构,有16根数据线,可以一次性传送16位的数据。

image-20221206101216853

image-20221206111645672

sub ax,bx	//ax的值减去bx

ds的值设定后,后续内存单元[···]自动认为在ds段地址中,这里看ds有点像全局变量

image-20221206112242991

3.4 mov、add、sub指令

mov指令的形式

mov 寄存器,数据		mov ax,8
mov 寄存器,寄存器		mov ax,bx
mov 寄存器,内存单元	mov ax,[0]
mov 内存单元,寄存器	mov [0],ax
mov 段寄存器,寄存器	mov ds,ax
mov 寄存器,段寄存器	mov ax,ds

add指令的形式

add 寄存器,数据		add ax,8
add 寄存器,寄存器		add ax,bx
add 寄存器,内存单元	add ax,[0]
add 内存单元,寄存器	add [0],ax

sub指令的形式

sub 寄存器,数据		sub ax,8
sub 寄存器,寄存器		sub ax,bx
sub 寄存器,内存单元	sub ax,[0]
sub 内存单元,寄存器	sub [0],ax

3.5 数据段

一组长度为N(N<=64KB)、地址连续、起始地址为16的倍数的内存单元当作专门存储数据的内存空间,定义为一个数据段。

image-20221206171237957

检测点3.1

(1)

image-20221207095116585

mov ax,1	//ax=0001H
mov ds,ax	//ds=0001H
mov ax,[0000]	//ax=2662H	(ax等于0001:0000-0001:0001地址位的值,0001:0000-0001:0001地址位等于00010H-00011H,同时可以发现0000:0010-0000:0011)
mov bx,[0001]	//bx=E626 (同上)
mov ax,bx	//ax=bx=E626
mov ax,[0000]	//ax=2662H
mov bx,[0002]	//bx=D6E6H
add ax,bx		//ax=ax+bx=FD48H
add ax,[0004]	//ax=ax+2ECCH=2C14H
mov ax,0		//ax=0000H
mov al,[0002]	//al=E6H,故ax=00E6H
mov bx,0		//bx=0000H
mov bl,[000c]	//bl=26,故bx=0026H
add al,bl 		//al=al+bl=E6+26=0CH,故ax=000CH

通过检测点3.1发现一个有趣的现象:0000:0010=0001:0000

(2)

image-20221207141944910

①②

初始值为CS=2000H,IP=0,DS=1000H,AX=0,BX=0,程序从CS:IP开始运行
mov ax,6622H	//ax=6622H
jmp 0ff0:0100	//CS:IP=0ff0:0100,跳转内存地址10000H
mov ax,2000H	//ax=2000H
mov ds,ax		//ds=2000H
mov ax,[0008]	//ax=C389
mov ax,[0002]	//ax=EA66

3.6 栈

栈的特点是:先进后出,后进先出

3.7 CPU提供的栈机制

PUSH(入栈)和POP(出栈)

push ax表示将寄存器ax中的数据送入栈

pop ax表示从栈顶取出数据送入ax

image-20221207150006980

段寄存器SS和寄存器SP存放栈顶的位置,其中栈顶的段地址存放在SS中,偏移地址存放在SP中,任意时刻,SS:SP指向栈顶元素,在执行push和pop指令时,CPU从SS和SP中得到栈顶的地址。

image-20221207161838885

image-20221207165858510

在8086CPU中,入栈时,栈顶从高地址向低地址方向增长。

注意,在pop指令中,虽然在SP=SP+2时,1000C-1000D中的值已经赋值给ax了,但它依然存在内存单元中,只是已经不在栈中,在下一次入栈时,将会被覆盖

问题3.6

image-20221207163337664

当栈为空时,SS=1000H,SP=10H

3.8 栈顶超界的问题

image-20221207170648908

image-20221207171242212

在8086CPU中,没有单独的寄存器负责栈是否超界的问题,需要开发者自行规范代码

超出栈的后果:pop在栈超界后,会导致大于10020H的地址从其他栈中释放出来,后续再用push时,将会出现数据覆盖的现象。

3.9 push、pop指令

push 寄存器	;将一个寄存器中的数据入栈
push 段寄存器	;将一个寄存器中的数据入栈
push 内存单元	;将一个内存字单元处的字入栈(栈操作以字为单位)
pop 寄存器		;出栈,用一个寄存器接收出栈的数据
pop 段寄存器	;出栈,用一个段寄存器接收出栈的数据
pop 内存单元	;出栈,用一个内存单元接收出栈的数据

image-20221207172136568

问题3.7

image-20221208005702507

mov ax,1000		
mov ss,ax		;设置栈的段地址,不能直接向ss中传数据
mov sp,0010		;设置栈顶的偏移地址,sp=0010H
push ax
push bx
push cx

问题3.8

image-20221208005808243

mov ax,1000
mov ss,ax
mov sp,0010
mov ax,001a
mov bx,001b
push ax
push bx
sub ax,ax	;将ax清零
			;sub ax,ax的机器码为2个字节
			;mov ax,0的机器码为3个字节
sub bx,bx
pop bx
pop ax

问题3.9

image-20221208010618305

mov ax,1000
mov ss,ax
mov sp,1010
mov ax,001a
mov bx,001b
push ax
push bx
pop ax
pop bx

问题3.10

image-20221208011003342

# 补充代码
mov ax,1000
mov ss,ax
mov sp,2	;根据sp-2写入原则,将初始地址定义到10002H上,则写入时,可以从10000H开始写入

3.10 栈段

在8086CPU中将一组内存单元定义为一个段。例如,将长度为N(N<=64KB)的一组地址连续、起始地址为16的倍数的内存单元,当做栈空间,从而定义一个栈段。

比如,将10010H~1001FH这段长度为16字节的内存空间作为栈来用,以栈的方式访问,则这个空间就成称为一个栈段,段地址为1001H,大小为16字节

问题3.11

image-20221208110432331

问题3.12

image-20221208110859027

根据问题3.11的结论,可以明显看出出,一个栈的大小,取决于SP的容量(相对于SP)

检测点3.2

(1)

image-20221208111432651

mov ax,2000
mov ss,ax
mov sp,0010

(2)

image-20221208111955311

mov ax,1000
mov ss,ax
mov sp,0000

实验2 用机器指令和汇编指令编程

1、预备知识:Debug的使用

d 段寄存器:偏移地址

-r ds
:1000
-d ds:0		;查看从1000:0开始的内存区间中的内容
-d ds:10 18	
-d cs:0		;查看当前代码段中的指令代码
-d ss:0		;查看当前栈段中的内容
-e ds:0 11 22 33 44 55 66	;在从1000:0开始的内存区间中写入数据
-u cs:0		;以汇编指令的形式,显示当前代码段中的代码
-a ds:0		;以汇编指令的形式,向从1000:0开始的内存单元中写入指令
mov ax,2000
mov ss,ax
mov sp,10	;设定2000:0000~2000:000F为栈空间,初始化栈顶

#在栈中压入两个数据
mov ax,3123
push ax
mov ax,3366
push ax

==debug的T命令在执行mov ss,ax命令后,紧接着mov sp,10自动执行了,这又被称为终端机制

(2)

image-20221208145855177

image-20221208145906540

原因是mov ss,ax mov sp,10两个指令时使用了中断机制,执行中断例程时,cpu会将一些中断例程使用的变量自动压栈到栈中。

4、第一个程序

4.1一个源程序从写出到执行的过程

第一步:编写汇编源程序

第二步:对源程序进行编译连接

第三步:执行可执行文件中的程序

4.2源程序

程序4.1

assume cs:codesg
codesg segment
	mov ax,0123H
	mov bx,0456H
	add ax,bx
	add ax,ax
	
	mov ax,4c00H
	int 21H
codesg ends
end

1、伪指令

汇编指令有对应的机器码,可以编译成机器指令,从而由CPU执行。伪指令没有对应的机器指令,最终不被CPU所执行。

(1)

段名 segment		//说明一个段开始
	
段名 ends		//一个段结束

一个有意义的汇编程序至少要有一个段,这个段用来存放代码。

(2)

end		//一个汇编程序的结束标记

segment···ends标记一个段的结束

end标记整个程序结束

(3)

assume		//将代码段codesg与段寄存器cs联系起来

假设某一段寄存器和程序中的某一个用segment…ends定义的段相关联

2、源程序中的“程序”

image-20221212103425171

3、标号

一个标号指代了一个地址。比如codesg

4、程序的结构

编程运算2^3

assume cs:abc	;(可选)abc当做代码段使用
;定义一个段,名称abc
abc segment
	mov ax,2	;写入汇编指令
	add ax,ax
	add ax,ax
abc ends
end

5、程序返回

image-20221212105819194

#在程序4.1中的命令返回代码
mov ax,4c00H
int 21H

image-20221212110334629

6、语法错误和逻辑错误

在上述4、程序的结构中,代码运行会引发一些问题,因为程序没有返回。这个错误在编译时不会表现出来。

修改后:

assume cs:abc	;(可选)abc当做代码段使用
;定义一个段,名称abc
abc segment
	mov ax,2	;写入汇编指令
	add ax,ax
	add ax,ax
	
	mov ax,4c00H
	int 21H
	
abc ends
end

4.3 编辑源程序

edit 1.asm
assume cs:codesg
	mov ax,0123h
	mov bx,0456h
	add ax,bx
	add ax,ax
	
	mov ax,4c00h
	int 21h
codesg ends
end

4.4 编译

该笔记采用微软masm汇编编译器

image-20221212165240454

image-20221212165216368

目标文件(.obj)、列表文件(.lst)、交叉引用文件(.crf)

其中目标文件是我们最终要得到的结果

4.5 连接

上一节中,将1.asm编译得到了1.obj,现在将1.obj连接为1.exe

利用link.exe进行连接,在bin目录下。

image-20221212171321506

nul.map提示输入映象文件的名称,该文件是连接程序将目标文件连接为执行文件过程中产生的中间结果,直接回车可以不生成这个文件

.lib提示输入库文件的名称。库文件里包含了一些可以调用的子程序,如果程序中调用了某一个库文件中的子程序,就需要在连接的时候,将库文件和目标文件连接到一起,生成可执行文件。

nul.def定义文件

连接的作用

image-20221212173020496

4.6 以简化的方式进行编译和连接

masm 1.asm;
link 1.obj;

4.7 1.exe的执行

这个程序只做了将数据送入寄存器和加法的操作。

4.8 谁将可执行文件中的程序装载进入内存并使它运行

image-20221213110244133

image-20221213110347639

image-20221213110441629

4.9 程序执行过程的跟踪

进入debug流程

debug 1.exe

在DOS系统中,exe文件程序的加载过程

image-20221213150357193

(1)程序加载后,ds中存放这程序所在内存区的段地址(DS=SA),这个内存区的偏移地址为0,则程序所在的内存区的地址为ds:0;

(2)内存区前256个字节中存放的是PSP,DOS用来和程序进行通信。从256字节处向后的空间存放的是程序。

ds可得到PSP的段地址SA,PSP的偏移地址为0,则物理地址为SAx16+0

因为PSP占256(100H)字节,所以程序的物理地址是:

SAx16+0+256=SAx16+16x16+0=(SA+16)x16+0

用段地址和偏移地址表示为:CS:IP=SA+10H:0

继续上面用debug调试1.exe

image-20221213160937023

通过r和u可以查看寄存器状态和内存内占用情况

image-20221213161313543

利用t可以一步一步执行,同时在执行到INT 21时,使用p命令,可以得到Program terminated normally(程序正常终止)的提示消息

实验3 编译、汇编、连接、跟踪

image-20221213161513437

程序最初运算的起点

image-20221213163854047

之后的每一步

-u ss:0 ss:sp=2000H:000AH

image-20221213170428921

pop ax ss:sp=2000H:000AH

image-20221213170558241

pop bx ss:sp=2000H:000CH

image-20221213171922125

push ax ss:sp=2000:000EH

image-20221213170935994

push bx ss:sp=2000:000CH

image-20221213171225599

pop ax ss:sp=2000:000AH

image-20221213172544066

pop bx ss:sp=2000:000CH

image-20221213172710718

这里有一个现象,pop取走空栈的内容,栈溢出了,导致栈的内容也改变了

assume cs:codesg	;将代码段codesg与cs联系起来
codesg segment		;代表一个段开始
	mov ax,2000H	;ax=2000H
	mov ss,ax		;ss=2000H,栈的段地址
	mov sp,0		;sp=0,栈的偏移地址
	add sp,10		;sp=0+10=10,栈顶为ss:sp,栈桥大小为sp=10
	pop ax			;取ss:sp位置的值,ax=0
	pop bx			;取ss:sp+2
	push ax			;取ss:sp+2+2-2
	push bx			;ss:sp+2+2-2-2
	pop ax
	pop bx
	
	mov ax,4c00H
	int 21H
codesg ends			;代表一个段结束

在源程序中,若不加H,则代表十进制

PSP的内容应该是在CS-10H:IP的位置

image-20221213163743692

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小白奋斗ing(小破站)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值