汇编学习笔记

汇编学习笔记

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

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

解释一下栈底部地址为什么是1000:FFFE,因为sp+2

问题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

5、[BX]和loop指令

1、[bx]和内存单元的描述

[bx]也表示一个内存单元,偏移地址在bx中,段地址在ds中

mov ax,[bx]

2、loop

循环

5.1 [BX]

mov ax,[bx] ;段地址SA默认在ds中,将SA:EA处的数据送入ax中。即:(ax)=((ds)*16+(bx))

mov [bx],ax ;将ax中的数据送入内存SA:EA处。即((ds)*16+(bx)=(ax))

问题5.1

image-20230109155417964

inc bx表示bx中的内容加1

mov ax,2000H	;ax=2000H
mov ds,ax		;ds=2000H
mov bx,1000H	;bx=1000H
mov ax,[bx]		;ax=00BE
inc bx			;bx=bx+1=1001H
inc bx			;bx=bx+1=1002H
mov [bx],ax		;21002H的内存地址是BE00(由小到大)
inc bx			;bx=bx+1=1003H
inc bx			;bx=bx+1=1004H
mov [bx],ax		;21004H的内存地址是BE00(由小到大)
inc bx			;bx=bx+1=1005H
mov [bx],al		;21005H的内存地址是BE(由小到大)
inc bx			;bx=bx+1=1006H
mov [bx],al		;21006H的内存地址是BE(由小到大)

image-20230109155837043

5.2 Loop指令

loop指令的格式是:loop标号,CPU执行loop指令的时候,要进行两步操作,①(cx)=(cx)-1;②判断cx中的值,不为零则转至标号处执行程序,如果为零则向下执行。

loop指令实现循环功能,cx中存放循环次数

程序 5.1

编程计算2^12,结果存在ax中

assume cs:codesg
codesg segment
	mov	ax,2
	mov	cx,11
s:	add ax,ax	;标号s,标识了一处指令
	loop s		;执行loop两步指令
	
	mov ax,4c00h
	int 21h
codesg ends

循环功能的程序框架

	mov cx,循环次数
s:
	循环执行的程序段
	loop s

程序5.2

编程计算123*236,结果存在ax中

assume cs:code
code segemt
	mov ax,0
	mov cx,123
s:	add ax,236
	loop s
	
	mov ax,4c00h
	int 21h
code ends
end

5.3 debug跟踪loop指令实现的循环程序

计算ffff:0006单元中的数乘以3,结果存储在dx中

程序5.3

assume cs:code
code segemt
	mov ax,0ffffh	;数值不能以字母开头
	mov ds,ax
	mov bx,6
	mov al,[bx]		;将ah为0,al为bx,是因为ax存储ffff:6-7单元的值
	mov ah,0
	mov cx,3
	mov dx,0
s:	add dx,ax
	loop s
	
	mov ax,4c00h
	int 21h
code ends
end

在汇编程序中,数值不能以字母开头

s等同于0012,loop s在源程序执行后,变成了loop 0012,在执行后,把IP设置为0012h。

5.4 Debug和汇编编译器masm对指令的不同处理

masm编译器会将"mov ax,[0]"当做"mov ax,0"处理

解决:可以写成"mov ax,ds:[0]",或者"mov ax,[bx]"

5.5 loop和[bx]的联合应用

问题:计算ffff:0~ffff:b单元中的数据的和,结果存储在dx中

因为ffff:0~ffff:b中的数据是8位的,不能直接加到16位的寄存器dx中

程序5.6

利用loop与[bx]的配合使用,实现上述问题

assume cs:codesg
codesg segment
    mov ax,0ffffh
    mov ds,ax
    mov bx,0
    mov dx,0
    mov cx,12
  s:
    mov al,[bx]
    mov ah,0
    add dx,ax
    inc bx		;ds:bx指向下一个单元
  loop s

    mov ax,4c00h
    int 21h
codesg ends
end

5.6 段前缀

用于显示地指向内存单元的段地址的"ds:" "cs:" "ss:" "es:",在汇编语言中称为段前缀。

mov ax,ds:[bx]
mov ax,cs:[bx]
mov ax,ss:[bx]
mov ax,es:[bx]
mov ax,ss:[0]
mov ax,cs:[0]

5.7 一段安全的空间

在8086模式中,随意向一段内存空间写入内容是很危险的,因为这段空间中可能存放着重要的系统数据或代码。

在dos方式下,一般情况,0:200~0:2ff空间中没有系统或其他程序的数据或代码

5.8 段前缀的使用

将内存ffff:0~ffff:b单元中的数据复制到0:200~0:20b单元中

分析:0:200~0:20b

assume cs:codesg
codesg segment
	mov bx,0	;(bx)=0,偏移地址从0开始
	mov cx,12	;(cx)=12,循环12次
s:	mov ax,offffh
	mov ds,ax	;(ds)=0ffffh
	mov dl,[bx]	;(dl)=((ds)*16+(bx)),将ffff:bx中的数据送入dl
	mov ax,0020h
	mov ds,ax	;(ds)=0020h
	mov [bx],dl	;((ds)*16+(bx))=(dl),将dl中的数据送入0020:bx
	
	inc bx	;(bx)=(bx)+1
	loop s
  	
  	mov ax,4c00h
  	int 21h
 code ends
 end

改进:

assume cs:code
code segment
	mov ax,0ffffh
	mov ds,ax	;(ds)=0ffffh
	mov ax,0020h
	mov es,ax	;(es)=0020h
	mov bx,0	;(bx)=0,此时ds:bx指向ffff:0,es:bx指向0020:0
	mov cx,12
s:	mov dl,[bx]	;(dl)=((ds)*16+(bx)),将ffff:bx中的数据送入dl
	mov es:[bx],dl	;((es)*16+(bx))=(dl),将dl中的数据送入0020:bx
	inc bx		;(bx)=(bx)+1
	loop s
	
	mov ax,4c00h
	int 21h
code ends
end

实验4 [bx]和loop的使用

assume cs:code
code segment
	mov ax,0020H
	mov ds,ax
	mov bx,0
	mov dx,0
	mov cx,64
s:	mov [bx],dx
	inc bx
	inc dx
	loop s
	
	mov ax,4c00h
	int 21h
code ends
end
assume cs:code
code segment
	mov ax,0020H
	mov ds,ax
	mov bx,0
	mov cx,64
s:	mov [bx],bx
	inc bx
	loop s
	
	mov ax,4c00h
	int 21h
code ends
end

(3)

assume cs:code
code segment
	mov ax,cs
	mov ds,ax
	mov ax,0020h
	mov es,ax
	mov bx,0
	mov cx,17H
s:	mov al,[bx]
	mov es:[bx],al
	inc bx
	loop s
	
	mov ax,4c00h
	int 21h
	
code ends
end

6、包含多个段的程序

程序取得所需空间的方法有两种,一是在加载程序的时候为程序分配,再就是程序在执行的过程中向系统申请

若要一个程序在被加载的时候取得所需的空间,则必须要在源程序中做出说明。在源程序中定义段来进行内存空间的获取。

6.1 在代码段汇中使用数据

dw定义字型数据,即define word

程序6.1

assume cs:code
code segment
	dw 0123h,0456h,0789h,0abch,0fedh,0cbah,0987h
	mov bx,0
	mov ax,0
	mov cx,8
s:	add ax,cs:[bx]
	add bx,2
	loop s
	
	mov ax,4c00h
	int 21h
code ends
end

因为dw定义的数据处于代码段的最开始,所以偏移地址为0,这8个数据就在代码段的偏移0、2、4、6、8、A、C、E处。程序运行时,它们的地址就是CS:0、CS:2、CS:4、CS6、CS:8、CS:A、CS:C、CS:E。

在代码段中,前面的16个字节是用dw定义的数据,从第16个字节开始才是汇编指令对应的机器码。

image-20230210170021449

image-20230210170035921

程序6.2

assume cs:code
code segment
	dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h
start:	mov bx,0
        mov ax,0
        mov cx,8
    s:	add ax,cs:[bx]
        add bx,2
        loop s
	
        mov ax,4c00h
        int 21h
code ends
end start

end作用,通知编译器程序结束,通知也通知编译器程序入口在哪(end start)

start是标号

程序框架

assume cs:code
code segment
	:
	:
	数据
	:
	:
start:
	:
	:
	代码
	:
	:
code ends
end start

6.2 在代码段中使用栈

image-20230210171320848

程序6.3

assume cs:codesg
codesg segment
	dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h
	dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
			;用dw定义16个字型数据,在程序加载后,将去得16个字的内存空间,
			;存放这个16个数据。在后面的程序中将这段空间当做栈来使用
start:	mov ax,cs
		mov ss,ax
		mov sp,30h
		

*简述上述知识点

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

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

mov 赋值、add 增加、sub 减少、jmp 跳跃、inc 加1、lea加载内存地址到寄存器、call 调用子程序
push 入栈、pop出栈

masm 1.asm;
link 1.obj;
编程(Edit)—>1.asm—>编译(masm)—>1.obj—>连接(link)—>1.exe—>加载(command)—>内存中的程序—>运行(CPU)

debug参数:
R查看、改变CPU寄存器的内容
D查看内存中的内容
E改写内存中的内容
U将机器指令翻译成汇编指令
T执行一条机器指令
A以汇编指令的格式在内存中写入一条机器指令
P自动重复执行循环中的指令,知道(cx)=0为止
g直接执行到执行位置,例如:'g 0016'=CS:0016

assume cs:codesg	;将代码段codesg与cs联系起来
codesg segment		;代表一个段开始
codesg ends			;代表一个段结束
start  ends start	;代表程序入口,start是标号

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

循环功能的程序框架
	mov cx,循环次数
s:
	循环执行的程序段
	loop s

"ds:" "cs:" "ss:" "es:",在汇编语言中称为段前缀。
dw定义字型数据,即define word

本书约定规章
()表示一个寄存器或一个内存单元中的内容
(ax)表示ax中的内容、(al)表示al中的内容
比如:(ax)表示ax中的内容、(al)表示al中的内容
"()"中的元素有三种类型:1、寄存器名	2、段寄存器名		3、内存单元的物理地址(一个20位数据)

idata表示常量
mov ax,[idata]代表 mov ax,[2]、mov ax,[3]等
mov bx,idata代表	mov bx,1、mov bx,2、mov bx,3等
posted @ 2023-12-06 22:45  DumpInfou  阅读(54)  评论(0编辑  收藏  举报