汇编语言基础笔记【必修表】

本文是16位CPU的汇编语法,用于汇编语言基础知识的学习,实验环境配置参考:

【&】 段前缀

ds是段寄存器,后面的偏移地址用寄存器或者数字表示。
ds:[bx]
当(ds)=10000h,(bx)=1200h,那么ds:[bx]=1001200h

【&】 栈访问

我们在CPU里面这样的逻辑过程:先进先出
我们设置栈寄存器的原因是方便我们对栈空间地址的管理和访问,并非用于储存栈内容。

我们指定一段内存空间作为栈空间,需要两个寄存器完成,SSSP,SS用于指定栈头,SP初始指定栈尾,使用的时候用于指定当前元素。SP我们管它叫 “栈指针寄存器”。

栈的进出对于指针而言是减增。

比如现在我们需要使用0-10空间作为栈,那么初始化的时候就需要SP=10,每当我们PUSH一次的时候,SP=SP-2倒退(-2是因为两个字节,PUSH是默认以字进行操作的)

初始化栈
和ds一样不能直接mov赋值

mov ax,1000h
mov ss,ax
mov sp,0010h

栈数据存放

push ax //把ax的数据存到栈空间里面
pop ax //弹出栈顶元素存到ax里面

【&】程序结束和格式

在8086PC上面使用这样的代码格式

assume cs:code
code segment
main:
...
mov 4c00h
int 21h
code ends
main end

ends是对于每一段的结束,而end则是对于整个程序的结束。
我们会使用

mov ax,4c00h
int 21h

来结束程序

【&】循环loop和cx

我们会把loop指令和寄存器cx配合使用,cx里面存放循环的次数,格式:loop 标号
例子:

mov cx,3
s:
add ax,2
loop s

【&】自增 inc 和 自减 dec

对于寄存器的内容自增或者自减。

inc bx
dec bx

【&】数据段的使用

如果我们先在程序固定下我们需要访问数据的空间,就需要在数据段定义。一般来说,我们存放数据的类型分为三种:db,dw,dd,分别是以字节存放,以字存放和以双字存放。
举例:
db 01h
dw 01h
dd 01h
在内存里面分别是
01 FF FF FF FF …
01 00 FF FF FF…
01 00 00 00 FF…

如果说:
db ‘welcome’
dw ‘welcome’
dd ‘welcome’

我们会在代码开始的时候申请数据段,我们可以随便命名。

【例子】把data1数据段的数据倒序拷贝到data2数据段里面

assume cs:code

data1 segment
dw 0123h,045h,0789h
data1 ends

data2 segment
dw 0,0,0,0
data2 ends

code segment
start:
mov ax,data2
mov ss,ax
mov sp,04h

mov ax,data1
mov ds,ax
mov bx,0
mov cx,3

s:push [bx]
add bx,2
loop s

mov 4c00h
int 21h

code ends
end start

【&】如何查看内存数据进行调试

首先,我们需要一段代码:

mov ax,数据段标号
mov ds,ax

然后打开调试模式,输入-t
看ax的数值,就是内存数据段的起始地址,比如是0770。
再输入:

-d 0770:00

就能看到内存的数据了

【&】逻辑运算指令

  • 逻辑运算:and or not xor
  • 移位:shl shr sal sar rol ror rcl rcr

shl和sal
逻辑左移和算术左移:

mov ax,00010101b
shl ax,1 //此时ax=00101010

最低位补0,最高位进入CF

汇编语言中 sal(算术百左移指令)和shl(逻辑左移指令)指令的寻址方式、控制移位方式等都一样,区度别其实只有一处:
SAL算术移位指令在执行时,实际上把操作数看成有符号数进行移位,最高位符号位移入CF,但本身保道持原值;其余位顺序左移,次高位被舍弃专。
SHL逻辑移位指令在执行时,实际上把操作数看成无符号数进行移位,属所有位顺序左移,最高位移入CF。
举例如下:

记住:sal是保持有符号数的最高位不变。

MOV AX,8001H//(AX)=1000 0000 0000 0001B
SAL AX,1 //(AX)=1000 0000 0000 0010B
MOV AX,8001H//(AX)=1000 0000 0000 0001B
SHL AX,1 //(AX)=0000 0000 0000 0010B

rol
和shl的区别在于,他是循环补位的,也就说在高位丢弃的位在低位补上。
在这里插入图片描述

mov al,40h        ; AL = 01000000b
rol al,1               ; AL = 10000000b, CF = 0
rol al,1               ; AL = 00000001b, CF = 1

rcl:
在这里插入图片描述
rcl和rol的区别在于补位的时候是否延后一位。

and指令和or指令和xor指令
mov al,01100011B
and al,10111111B
al=00100011B

mov al,01100001B
or al,10111101B
al=11111101B

mov al,01100001B
xor al,10111101B
al=11011100B

【实验】字母大小写转换
我们首先知道:‘A’+20h=‘a’,我们当然可以对其进行一次加法运算,但是这样操作的前提是我们知道他是大写还是小写,一旦设计到条件判断的语句就会消耗大量的资源,我们可以利用逻辑运算来实现大小写转换。

我们对20h分析:20h=2*16+0=32

十进制的32刚好是二进制的第六位,也就是说第六位的数字是1和0,导致整个数字相差20h。比如:【??1????】和 【??0????】之间就相差20h。

当我们对一个字符串db 'welcome’操作的时候,我们如果想让l这个字母大写,如果他是大写那就不修改,第六位的转变:
0->0
1->0
其他:
?->?

所以,设置逻辑运算:
and al,11011111B

完整代码 :

assume cs:code

data segment
	db 'welcome$'
data ends
 
code segment
main:
	mov bx,0
	mov ax,data
	mov ds,ax
	
	mov al,ds:[0]
	
	and al,11011111B
	mov ds:[0],al
	
	
	lea dx,ds:[0]	;需要把字符串地址给dx
	mov ah,9 ;9是输出字符功能
	int 21h ;21号中断是执行dos系统事件
	ret
		
code ends
end main

输出’Welcome’

【&】si,di,bp寄存器的使用

这些寄存器都是用于偏移地址,注意的是si和di是不能拆开两个八位的寄存器使用的。
格式:
mov ax,[bx/bp+si/di+idata]

【实验】字符串的复制

assume cs:code

data segment
	str1 db 'welcome$'
	str2 db 0,0,0,0,0,0,0,0
data ends
 
code segment
main:
	mov ax,data
	mov ds,ax
	mov si,0
	
	mov cx,8
	s: mov al,ds:[si]
	mov ds:[str2+si],al
	add si,1
	loop s
	
	lea dx,ds:[str2]	;需要把字符串地址给dx
	mov ah,9 ;9是输出字符功能
	int 21h ;21号中断是执行dos系统事件
	ret
		
code ends
end main

实现把str1的数据复制到str2
需要注意的是:若以db单位储存字符,16bit=2B,2个字母,一个寄存器能储存两个字母,如果是以dw就是一个字母。

【&】访问内存区分字节和字

mov word ptr ds:[0],1
mov byte ptr ds[0],1

第一种存到内存里面:01 00 FF FF FF FF
第二种存到内存里面:01 FF

【&】除法指令 乘法指令

除法 div
乘法 mul

除法:结果储存在ax的拆开两半al和ah,al放商,ah放余数
乘法:al放被乘数,ax放结果

div byte ptr ds:[0]
(al)=(ax)/((ds)*16+0)的商
(ah)=(ax)/((ds)*16+0)的余数

格式:mul reg
mul 内存单元

例子:
mul byte ptr ds:[0]
(ax)=(al)*((ds)*16+0)

【&】拓展数据 dup指令

data segment
	db 3 dup (0,1,2)= db 0,1,2,0,1,2,0,1,2
	db 3 dup ('abc','ABC')
data ends

【&】取标志的偏移地址 offset指令和坑nop

这样就相当于吧start的偏移地址0存到了ax里面

start:
mov ax,offset start

代码坑:nop
【实验】复制指令到空白的代码坑

assume cs:codesg
codesg segment
s:
mov ax,bx
mov si,offset s
mov di,offset s0
mov ax,cs:[si]
mov cs:[di],ax

s0:
nop ;相当于萝卜坑
nop
codeseg ends
end s

【&】无条件转移指令 jmp

使用格式:jmp 标号段/内存地址

例子1:

mov ax,0123h
mov ds:[0],ax
jmp word ptr ds:[0]

例子2:

start:
mov ax,0
jmp short s
mov ax,1
s:
inc ax
code ends
end start

jmp可以同时修改cs和ip,例子:

mov ax,0123h
mov ds:[0],ax
mov word ptr ds:[2],0
jmp dword ptr ds:[0]

cs:ip=0000:0123

【&】有条件转移指令 jcxz

条件看cx。(cx是个条件工具人,无论是loop还是jcxz都需要看cx作为标志)

格式:jcxz 标号/内存地址

假如cx=0的时候执行,反之什么也不做。

【&】返回出栈 ret 指令和调用入栈指令 call

ret指令事利用栈中的数据修改ip的数据从而实现转移,一般和call配合使用

格式:

call s
s:
xxxx
ret

假如我们需要参数传递,我们一般利用内存空间传入参数,现在写一个把所指单元的字母小写改成大写的函数。

!!!!!注意的事:函数体需要写在main的ret的后面!!!!!

func_head:
push si ;保存参数
call func_main
pop si ;恢复参数
ret

func_main:
mov al,ds:[si]
and al,11011111b
mov ds:[si],al
ret

完整版:

assume cs:code

data segment
str1 db 'welcome$'
data ends

code segment
main:
mov ax,data
mov ds,ax

mov si,0
mov cx,7
s:
call func_head
inc si
loop s


lea dx,str1
mov ah,9 ;9是输出字符功能
int 21h ;21号中断是执行dos系统事件
ret

func_head:
push si
call func_main
pop si
ret

func_main:
mov al,ds:[si]
and al,11011111b
mov ds:[si],al
ret

code ends
end main

把‘welcome’改成‘WELCOME’

【&】标志寄存器 flag

flag寄存器是按照位来起作用的。
这些都是布尔标志。

0123456789101112131415
CFPFAFZFSFTFIFDFOF
是否进位是否是偶数是否是0是否是负数是否中断是否递减是否溢出

举例子:
比如:
#zf是零标志位,如果某个指令的结果是0,zf=true
比如:
mov ax,1
sub ax,1
那么这个时候zf=1

#pf是奇偶标志位,是否是偶数
mov al,1
add al,10
al=11
pf=0

#sf是正负标志,是否是负数
mov al,10000001b
add al,1

#cf是进位标志位,是否进位

首先我们必须知道为什么要进位和什么时候进位?
当结果溢出了我们原本所给容器的范围的时候就会发生进位。

mov al,98h
add al,al

al=30h,cf=1

另外cf也会记录做减法的时候从高位的借位。
mov al,97h
sub al,98h

这个时候al=ff,cf=1

#of 溢出标志
如何区分of和cf的差异?
cf是对于无符号数有意义的标志位,而of是对于有符号数的有意义的位,也就是说我们用负数操作对于cf是没有意义的。

举个例子:
mov al,98
add al,99

此时,cf=0,of=1,因为,按无符号数看的话,兵没有进位发生,而对于有符号数而言已经溢出了。

【&】cmp指令

cmp实际上减法指令的变种,但是他会对减法结果进行一次判断,不储存减法结果。

用法:

mov ax,8
mov bx,3
cmp ax,bx

执行之后:
ax=8 zf=0 cf=0

通过看zf和cf的标志位,我们就可以知道ax和bx数值的大小关系。
zf=true → \rightarrow ax = = =bx
zf=false → \rightarrow ax ≠ \neq =bx

cf=true → \rightarrow ax < < <bx
cf=false → \rightarrow ax > = >= >=bx

无非就是cf和zf的组合使用。

【&】cmp的搭配跳转指令们 jne je jna ja jnb jb

je 等于就转
jne 不等于就转
jb 低于就转
jnb 不低于就转
ja 高于就转
jna 不高于就转

below和above

举例子:
cmp ah,bh
je s
xxxx
s:
xxxx

【&】rep指令和movsb

movsb指令用于把字节从ds:si 搬到es:di;rep是repeat的意思,rep movsb 就是多次搬运。

【&】DF递减标志

对于寄存器的第十位的df,是方向标志位,表示是否递减,对于用于控制每次操作之后的si和di递增和递减。

指令cld可以让df=0,std可以让df=1

指令movsb用于把某个内存单元复制到某个内存单元
((es)*16+(di))=((ds)*16+(si))
当这轮操作之后,看df?=0,
df=0
si++
di++
反之
si–
di–

【例子】用movsb指令把某个内存单元都知道某个内存单元

assume cs:code

data segment
str1 db 'welcome$'
str2 db 16 dup(0)

data ends


code segment
main:
mov ax,data
mov ds,ax

mov si,0
mov es,ax
mov di,8
mov cx,8
cld
rep movsb

lea dx,str2
mov ah,9
int 21h

ret

code ends
end main

输出’welcome’

【&】键盘输入输出

使用

mov ah,0
int 16h

输入内容存到al里面

使用

lea dx,起始地址
mov ah,9
int21h

把地址的内容输出到屏幕上

示例代码:

assume cs:code

data segment
char db '$$'
data ends

code segment
main:

s:
mov ax,data
mov ds,ax

mov ah,0
int 16h

mov ds:[char+0],al
lea dx,char
mov ah,9
int 21h
jmp s

code ends
end main

当我们需要检验当前输入的字符是什么的时候,我们可以利用
cmp al,ascii数字码
比较大小,在利用jne,jbe等命令进行跳转

【&】串指令

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值