汇编语言学习笔记

本文详细介绍了汇编语言的基础知识,包括寄存器、内存访问、指令集、程序结构及编译运行流程。探讨了Debug工具的使用,解释了BX、LOOP指令的运用,以及如何在MASM和Debug中正确处理内存访问。文章还涵盖了段前缀、数据处理、转移指令、CALL和RET指令、标志寄存器的功能,以及中断和端口的概念。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

文章目录

一、Dosbox debug


输入debug进入debug模式

命令作用
R查看或改变寄存器内容。
输入r查看;输入r ax(寄存器名)后在后输入值修改寄存器的值
D查看内存中的内容。
d 段地址:偏移地址输出从该地址开始的128个内存单元的内容;d地址从预设地址开始;d 段地址:起始偏移地址 结尾偏移地址指定查看范围,可以用这种方式查看单个内存单元中的内容。
输出内容包括左:起始地址;中:十六进制内容;右:对应的可显示的ASCII码字符。
E改写内存中的内容。
e 起始地址 数据 数据 数据 ···e 起始地址,在.后输入数据,按空格继续输入,回车结束输入,不输入数据直接空格表示不修改数据。
e 起始地址 "xx" "xx"...写入字符串
U查看机器码所对应的汇编指令
u 起始地址
输出包括左:地址;中:机器指令;右:对应的汇编指令。
T执行指令,会显示所有寄存器的内容。
A以汇编指令的形式在内存中写入机器指令。
a 起始地址进入输入模式,在每个地址后输入机器指令,enter继续输入下一条指令,直接enter表示操作结束。

二、寄存器(内存访问)

1、内存中字的存储

16位寄存器存储一个字,高八位存放高位字节,低八位存放低位字节

2、DS

访问内存单元时,CPU自动读取ds中的数据为内存单元的段地址,[]中的数据表示偏移地址,所以[x]所表示的内存单元是ds:x。
数据传入段寄存器ds的方法只有通过一般寄存器存入段寄存器,不能直接数据输入,如:

mov bx,1000H
mov ds,bx

3、栈机制

任意时刻,SS:SP指向栈顶元素
push和pop指令执行时,由SS:SP得到栈顶地址。
push和pop的对象可以是寄存器、内存单元、段寄存器。

三、第一个程序


1、汇编语言源程序

(1)伪指令

sgment & ends

段的开始和结束,使用方式为

xxx segment
	your code here
xxx ends
assume

假设某个寄存器和某个定义的段相连

assume cs:xxx

将xxx和cs寄存器关联

(2)程序的结构

assume cs:xxx
xxx segment
	your code here
xxx ends
end

(3)程序返回

在程序的末尾添加返回的程序段

mov ax,4c00H
int 21H

(4)完整代码

assume cs:xxx
xxx segment 
	your code here
	mov ax,4c00H
	int 21H
xxx ends
end

2、编译运行源程序

(1)程序的编辑

可以在edit中编辑,或者更常用的是在dosbox工作目录新建txt文件输入代码,然后修改后缀为asm文件

(2)程序的编译

输入masm
在提示source file 处输入文件名(省略.asm后缀)
后面全部直接enter
会看到工作目录下产生.OBJ文件

(3)程序的连接

输入link
在提示Object Modules处输入文件名(省略.OBJ后缀)
后面全部直接enter
会看到工作目录下产生.exe文件
连接提示没有栈段可以忽略

(4)便捷的编译和连接

C:\>masm 文件名(忽略.asm后缀);
C:\>link 文件名(忽略.OBJ后缀);

(5)程序的运行和跟踪

直接输入文件名(忽略exe后缀)即运行了exe程序
输入debug 文件名.exe跟踪程序
使用R命令查看各寄存器设置情况
使用U命令查看其他指令
使用T命令单步执行程序中每一条命令
到了int 21使用P命令执行程序,显示出Program terminated normally表示程序正常结束。
使用Q命令返回command模式。

四、[bx]和loop

loop和[bx]的结合可以实现内存单元中数据的累加。

1、[bx]

bx寄存器中存放偏移地址EA,与ds中段地址SA结合,内存单元[bx]即表示SA:EA。

2、loop

循环,每次loop都会使cx中数据减一,判断cx中数据是否为0,简而言之,循环cx次。
使用方式:

	mov cx,次数
s:	your code here
	loop s

s是标号,可自定义。

3、debug和masm对指令的不同处理

masm将mov ax,[0]当作mov ax,0处理
解决方式:mov ax,ds:[0]mov bx,0 mov ax,[bx]

4、段前缀

与ds类似,段前缀有ds,cs,ss,es。
使用时需要在指令中显式给出
mov ax,cs:[bx]

5、安全空间

0:200~0:2ff的256个字节中一般都是0,没有系统或其他程序的代码,安全。

五、包含多个段的程序

1、dw和start

(1)dw

define word,定义字型数据。
程序在运行时CS中存放代码段的段地址,所以dw定义的数据从该代码段开始,偏移地址为0。

(2)start

dw定义数据占了程序开始代码段的字节,我们需要start来确定程序开始的地方。
start不是指令的命名,汇编语言中的end决定了代码开始,end为空则默认从CS开始处执行,end后的命名处就是程序的开始处

code segment
x:your start code here 
code ends
end x(end 决定 start)

(3)现在的程序框架

assume cs:code
code segment
		.
		.
		.
		dw 数据
		.
		.
		.
start 	.
		.
		.
		your code here
		.
		.
		.
code ends 
end start 

2、放入不同的段

为了区分数据、指令以及栈,我们将这三者放入不同的段,具体如下:

assume cs:code,ds:data,ss:stack
data segment
	dw 数据
data ends 
stack segment 
	dw 数据
stack ends
code segment 
start:	your code here
		mov ax,4c00h
		int 21h 
code ends 
end start 

注意到段地址ds、cs、ss都存放了指定内容,在code代码段中可以使用内存单元直接调用。

六、更灵活地定位内存地址

1、and & or

和mov add等指令类似,按位与,按位或

2、字符形式数据

用’…'的形式指出数据是以字符的给出的

3、大小写转换问题

A从41H开始,a从61H开始,两者相差20H,由ans和or的应用,可以得到
将al中字符变大写:and al,11011111B
将al中字符变小写:or al,00100000B

4、[bx+…]

(1)[bx+idata]

表示内存单元,偏移地址为bx中数据加idata

(2)si&di

和bx功能相近似的寄存器

(3)[bx+si],[bx+di]

偏移地址为bx中数值加上si/di中数值

七、数据处理的两个基本问题

定义描述型符号reg表示寄存器,sreg表示段寄存器

1、bx\si\di\bp

这四个寄存器都可以单独出现或者以四种组合:
bx和si,bx和di,bp和si,bp和di

关于bp:在使用bp时,如果没有显式地给出段地址,默认的段地址在ss中。

2、数据位置的表达

立即数:数据或者字符
寄存器
段地址SA和偏移地址EA:偏移地址[…]表示,段地址可显式给出或者默认。

数据访问长度

两个尺寸的数据,byte字和word字节

(1)通过寄存器名指定

ax,bx,cx等表示字操作
al,bl,cl等表示字节操作

(2)在没有寄存器名存在下,用X ptr

X指明内存单元长度,X可以是word或者byte,举例:
inc word ptr [bx]
add byte ptr [bx],2

3、div

除法指令
格式:div 内存单元div reg

(1)除数

8位或者16位,在一个reg或者内存单元中

(2)被除数

如果除数为8位,则被除数为16位,默认在AX中;
如果除数为16位,则被除数为32位,DX存放高16位,AX存放低16位。

(3)结果

如果除数为8位,AL存储除法操作的商,AH存放除法操作的余数;
如果除数为16位,AX存储除法操作的商,DX存放除法操作的余数。

4、定义数据

db定义字节型数据
dw定义字型数据
dd定义双字型数据

dup 定义多个重复的数据
例如:db 3 dup (0,1,2)定义9个字节,相当于db 0,1,2,0,1,2,0,1,2

八、转移指令

1、offset

取得标号的偏移地址,如:
s:mov ax,offset s取得s处的偏移地址。

2、jmp

无条件转移指令,可以只修改ip,也可以同时修改CS和IP
jmp需要两种信息:转移的目的地址、转移的距离(段间转移、段内短转移、段内近转移)

(1)根据位移进行的jmp

jmp short 标号转移到标号处执行指令,段内短转移,实现8位内的位移。
jmp near ptr 标号段内近转移,16位内的位移。

机器码编译出的标号不是转移的地址,而是转移的位移。

(2)转移的目的地址在指令中的jmp

jmp far ptr 指令实现段间转移,(CS)=标号所在段的段地址,(IP)=标号在段中的偏移地址。

(3)转移地址在寄存器中的jmp

jmp 16位reg(IP)=(16位reg)

(4)转移地址在内存中的jmp

jmp word ptr 内存单元地址

段内转移,内存单元中存放的是转移的偏移地址

jmp dword ptr 内存单元地址

段间转移,(CS)=(内存单元地址+2)(IP)=(内存单元地址)

3、jcxz

有条件转移指令,都是短转移,机器码中是转移位移而非地址,8位内转移。
格式:jcxz 标号
功能:如果(cx)=0,转移到标号处执行;如果(cx) ≠ \ne = 0,什么都不做。

4、loop

loop也是短转移,8位内,机器码中是转移位移。

5、关于屏幕上显示内容

B8000H~BFFFFH是80 × \times × 25彩色字符模式的显示缓冲区,向这个内存地址写入数据会立即出现在显示器上。
缓冲区中内容,每个数据后跟着的是该数据的属性字节,设置前景色和背景色,然后再是下一个数据。因此,偶数位存放数据,奇数位存放属性。
属性字含义:
第一位表示是否闪烁(全屏模式下可见)
第二三四位表示背景色RGB
第五位表示是否高亮
第六七八位表示前景色RGB

九、CALL和RET指令

1、ret和retf

ret用栈中数据修改IP的内容,实现近转移
retf用栈中数据修改CS和IP内容,实现远转移
ret:相当于pop IP
retf相当于

pop IP
pop CS

2、call

将当前的IP或者CS和IP压入栈中,然后转移

(1)依据位移进行转移

call 标号将当前的IP压栈后,转到标号处执行指令。

(2)转移的目的地址在指令中

call far ptr 标号实现段间转移,将当前CS,IP压栈

(3)转移的地址在寄存器中

call 16位reg将当前IP压栈,然后跳转

(4)转移地址在内存中

call word ptr 内存单元地址将当前IP压栈,跳转
call dword ptr 内存单元地址将当前IP和CS压栈,跳转

3、call和ret使用

二者结合使用可以实现调用子程序

	call x
x:	your code here 
	ret

4、mul

乘法,格式为:mul reg/内存单元
两个相乘的数要么都是8位,要么都是16位
若都是8位,一个默认在AL中,结果默认放在AX中
若都是16位,一个默认在AX中,结果高位在DX中,低位在AX中

十、标志寄存器

1、flag寄存器结构

1514131211109876543210
OFDFIFTFSFZFAFPFCF

空位不具有任何含义

2、各标志含义

(1)ZF

记录相关结果是否为0,如果是0,则ZF为1;反之为0

(2)PF

记录结果bit位中1的个数的奇偶性,如果是偶数,则为1;反之为0

(3)SF

记录结果是否为负,如果是负,则为1;反之为0
对于有无符号具体看指令

(4)CF

加法记录结果有效位向更高位的进位值;
减法记录向更高位的借位值;
仅对无符号数有意义

(5)OF

记录结果是否溢出,仅对有符号数有意义

3、adc

格式:adc 操作对象1,操作对象2
功能:操作对象1=操作对象1+操作对象2+CF
adc的作用是加法二次进位运算

4、sbb

格式:sbb 操作对象1,操作对象2
功能:操作对象1=操作对象1-操作对象2-CF
应用思路和adc类似

5、cmp

格式:cmp 操作对象1,操作对象2
功能:计算操作对象1-操作对象2,不保存结果,仅对标志寄存器设置

6、条件转移指令

指令含义检测的相关标志位
je等于则转移zf=1
jne不能等于则转移zf=0
jb低于则转移cf=1
jnb不低于则转移cf=0
ja高于则转移cf=0且zf=0
jna不高于则转移cf=1或zf=1

7、df标志和串传送指令

df控制每次操作后si、di的增减。
df=0,递增
df=1,递减

(1)movsb

每次位移1位,将ds:si指向的内存单元中的字节送入es:di中

(2)movsw

每次位移2位,将ds:si指向的内存单元中的字送入es:di中

(3)rep

movsb和movsw一般和rep配合使用
rep movsb就是根据cx的值循环执行串传送

8、pushf和popf

pushf是将标志寄存器的值压栈
popf是从栈中弹出数据送入标志寄存器中

9、标志寄存器在debug中的表示

标志值为1的标记值为0的标记
OFOVNV
SFNGPL
ZFZRNZ
PFPEPO
CFCYNC
DFDNUP

十一、内中断

1、内中断的产生

(1)除法错误,比如溢出 中断类型码:0
(2)单步执行 中断类型码:1
(3)执行into命令 中断类型码:4
(4)执行int命令 中断类型码:n,是int指令后的立即数

2、中断向量表

在内存0000:0000 到 0000:03FF的1024个单元中存放着256个中断源所对应的中断处理程序的入口

3、中断处理过程

(1)取得中断类型码N
(2)pushf
(3)(标志寄存器第八位)TF=0,(标志寄存器第九位)IF=0
(4)push CS
(5)push IP
(6)(IP)=(N4),(CS)=(N4+2)

4、中断处理程序和iret

(1)中断处理程序

保存用到的寄存器->处理中断->恢复用到的寄存器->用iret返回

(2)iret

等同于

pop IP
pop CS
popf

与中断处理过程压栈顺序相对应

5、中断处理程序编写

assume cs:code

code segment 
start:	mov ax,cs
		mov ds,ax
		mov si,offset do0					设置ds:si指向源地址
		
		mov ax,0
		mov es,ax
		mov di,200h							设置es:di指向目的地址0000:0200h
		
		mov cx,offset do0end-offset do0		设置cx为传输长度,’-‘是编译器识别的运算符号,实现减法
		cld 								设置传输方向为正
		rep movsb							传输源程序到目的地址
		
		mov ax,0
		mov es,ax
		mov word ptr es:[0*4],200h
		mov word ptr es:[0*4+2],0 			设置中断向量
		
		mov ax,4c00h
		int 21h
	
	do0:jmp short do0start
		db"overflow!"						设置字符do0中,避免被其他程序覆盖
													前一条指令占两个字节,所以字符偏移地址为202h
	do0start:	mov ax,cs
				mov ds,ax
				mov si,202h					设置ds:si指向字符串
				mov ax,0b800h
				mov es,ax
				mov di,12*160+36*2			设置ex:di指向显存空间的中间位置
				mov cx,9					设置cx为字符串长度
			
			s:	mov al,[si]
				mov es:[di],al
				inc si
				add di,2
				loop s
				
				mov ax,4c00h
				int 21h						中断处理完直接返回dos
	do0end:nop
	code ends
	end start

6、单步中断

CPU在执行完一条指令后,如果检测到标志寄存器的TF位为1,则产生单步中断,中断类型码为1。

关于debug
debug的实质就是一种单步中断,debug提供了单步中断的中断处理程序,功能即为显示寄存器中的所有内容并等待输入命令。在使用t命令时debug将TF设置为1,引发单步中断。

为了避免CPU每次执行完一条指令判断TF=1而产生死循环,我们在引发中断处理程序前将TF设置为0

十二、int指令

1、int指令

int nn为中断类型码。
语句执行过程如下:
(1)取中断类型码n
(2)pushf(标志寄存器入栈),IF=0,TF=0
(3)CS,IP入栈
(4)(IP)=(n4),(CS)=(n4+2)

2、BIOS 提供的中断例程

BIOS(基本输入输出系统),是系统板的ROM中存放的一套程序,主要包含:
(1)硬件系统的检测和初始化程序
(2)外部中断和内部中断的例程
(3)对硬件设备进行I/O操作的中断例程
(4)其他和硬件系统相关的中断例程

3、DOS中断例程

int 21h是dos提供的中断例程
我们之前一直使用的是该中断例程的4ch号功能,程序返回

mov ah,4ch
mov al,0
int 21h

十三、端口

CPU端口地址范围为0~65535,64KB各不同的端口

1、端口的读写

端口的读写指令只有两条:
in从端口读取数据
out往端口写入数据

只能用ax或al存放读入或写入的数据,访问8位端口时用al,16位端口时用ax
对0~255的端口进行读写时,可以用立即数表示端口地址,如:in al,20h
对256~65535的端口进行读写时,端口号放在dx中,如:mov dx,3f8h in al,dx

2、shl和shr指令

逻辑位移指令

(1)shl

逻辑左移指令,功能为
(1)将一个寄存器或内存单元中的数据向左移位
(2)将最后移出的一位写入CF中
(3)最低位用0补充

如果移位数大于1,必须将移位数放在cl中

(2)shr

逻辑右移指令,功能为
(1)将一个寄存器或内存单元中的数据向左移位
(2)将最后移出的一位写入CF中
(3)最低位用0补充

如果移位数大于1,必须将移位数放在cl中

3、CMOS RAM芯片

该芯片包含一个时钟和有128个存储单元的RAM存储器
128个字节的RAM中,0~0dh单元用来保存时间信息
该芯片有两个端口:70h和71h
70h存放要访问的CMOS RAM 单元的地址,71h存放从选定的CMOS RAM单元中读写的数据,所以,CPU对CMOS RAM 读写分为两步:
(1)将单元号送入端口70h out 70h,al
(2)从端口71h读出该单元号的内容in al,71h

4、CMOS RAM中存储的时间信息

时间单元:

024789

时间信息用两个BCD码表示两位十进制数,如00010100b表示14

关于BCD码:用4位二进制数表示1位十进制数

5、显示月份

(1)从8号单元读取BCD码

mov al,8
out 70h,al
in al,70h

(2)BCD码转化为十进制ASCII码形式

mov ah,al
mov cl,4
shr ah,cl	;ah中是月份的十位数码值
and al,00001111b	;al中是月份的个位数码值

BCD码值+30h就是十进制数对应的ASCII码

十四、外中断

1、接口芯片和端口

外设的输入不直接送入内存和CPU,而是送入相关的接口芯片的端口中
CPU向外设的输出内容或控制命令也是先送入端口中,再由相关芯片送到外设。

2、外中断信息

外设输入到达,相关芯片向CPU发出终端信息,CPU检测到后引发终端过程处理外设的输入。

(1)可屏蔽中断

CPU可以不响应的外中断
IF=1,CPU在执行完当前指令后响应中断
IF=0,不响应

可屏蔽中断的中断类型码通过数据总线送入CPU,其他过程和内中断引发的过程相同
引发过程中将IF置为0,可以使得在进入中断程序后,禁止其他的可屏蔽中断
如果要在中断处理程序中处理可屏蔽中断,可以使用如下指令
sti设置IF=1
cli设置IF=0

(2)不可屏蔽中断

CPU必须响应的外中断,中断类型码固定为2,所以中断过程种不需要取中断类型码。

3、PC机键盘的处理过程

(1)键盘输入

键盘上的按键状态变化会产生扫描码被送到相关接口芯片的寄存器中,寄存器的端口地址为60h
按下一个键产生的扫描码称为通码,松开一个键产生的扫描码称为断码,通码第7位为0,断码第7位为1,即断码=通码+80h

(2)引发9号中断

键盘的输入到达60h端口时,相关芯片向CPU发出中断类型码为9的可屏蔽终端信息,若IF=1则引发中断过程

(3)执行int 9中断例程

BIOS提供了int 9中断例程
1、读取60h端口中的扫描码
2、如果是字符键的扫描码,将该扫描码和它对应的ASCII码送入内存中的BIOS缓冲区;如果是控制键(如Ctrl),将其转化为状态字节(用二进制位记录控制键和切换键状态的字节)写入内存中存储状态字节的单元
3、对键盘系统进行相关控制,比如向相关芯片发出应答信息

关于BIOS键盘缓冲区
BIOS用于存放int 9 中断例程所接收的键盘输入的内存区,可以存储15个键盘输入,一个键盘输入用一个字单元存放,高位字节存放扫描码,低位字节存放字符码。

关于键盘状态字节
0040:17单元存放键盘状态字节,各信息如下
0:置1表示按下右shift键
1:置1表示按下左shift键
2:置1表示按下Ctrl键
3:置1表示按下Alt键
4:ScrollLock状态,置1表示Scroll指示灯亮
5:NumLock状态,置1表示小键盘输入的是数字
6:CapsLock状态,置1表示输入大写字母
7:Insert状态,置1表示处于删除态

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值