汇编语言

基础知识

汇编语言由汇编指令,伪指令,其他符号组成, 其中核心是汇编指令,也可以称之为机器码的助记符。

CPU的信息交互方式分为三种:

  • 存储单元的地址(地址信息)
  • 读写的指令(控制信息)
  • 读写的数据(数据信息)

CPU与其他设备连接的导线分为三类:

  • 地址总线:决定CPU的寻址能力
  • 数据总线:数据传送量
  • 控制总线:对系统的控制能力

CPU组成:运算器,控制器,寄存器,这些器件依靠总线相连接。

Debug指令:
在这里插入图片描述

寄存器

地址加法器合成物理地址的方法:物理地址=段地址*16+偏移地址

CPU访问内存单元时,由段寄存器提供内存的地址,CS为指令的段地址,IP为指令的偏移地址,共同决定了指令的地址。任意时刻,CPU将CS:IP地址指向的内容当作指令执行.

ax,bx,cx,dx为通用寄存器,可以直接与数据交互,而向cs,ds这种段寄存器则不能将数据直接送入,而应该先将数据传入通用寄存器,再将其传入段寄存器.

//例如要将1000传给ds
mov ax,1000
mov ds,ax

1个字为16位,占2个字节,8086CPU一个内存单元为8位,因此要用两个连续的存储单元存储1个字,高位存高字节,低位存低字节。

用mov,pop,push 指令访问内存时,可以只给出偏移地址,段地址默认存储在DS中。

寄存器(内存访问)

栈操作:

  • 段寄存器SS:存放栈顶的段地址
  • 寄存器SP:存放栈顶的偏移地址

任意时刻,SS:SP指向栈顶元素。

入栈操作push

  • 偏移地址向上减小,SP=SP-2
  • 将寄存器中的数据压入栈中

出栈操作pop:

  • 先读取栈顶指向的数据
  • 偏移地址向下增加,SP=SP+2
    在这里插入图片描述

数据恢复:

在这里插入图片描述

	mov ax,1000H
	mov ss,ax //段地址
	mov sp,0010H //偏移地址,栈是空的,栈顶指向 1000F+1=100010
	mov ax,001AH
	mov bx,001BH
	push ax
	push bx
	mov ax,0 //清零
	mov bx,0 //清零
	pop bx //后进先出
	pop ax

  • 数据段:段地址放在DS中,用mov,add, sub访问内存单元时,CPU将数据段中的内容当作数据来访问
  • 代码段:段地址放在CS中,第一条指令的偏移地址放在IP中,CPU执行代码段中的指令
  • 栈段:偏移地址放在SS中,栈顶的偏移地址放在SP中,CPU在进行栈操作时,将栈段当作栈空间进行访问.

[bx]和loop指令

bx指令:

  • bx存放数据的是偏移地址,段地址存放在ds中
  • mov ax,[bx] ,表示将(ds*16+bx)地址的数据放入ax

Debug和MASM区别:

  • Debug中,mov ax,[0] 表示的偏移地址的数据放入ax
  • MASM中,mov ax,[0] 等价于 mov ax,0

将地址数据放入寄存器的两种方式:

;1.先给bx赋值,mov bx,0   mov al,[bx]
;2.添加段地址, mov al,ds:[0]

;一个内存单元为8位,而一个ax,bx为16位,因此要用al,bl保存数据

assume cs:codesg

codesg segment

start:
	mov ax,2000H ;2000不能省略H
	mov ds,ax ;段地址赋值
	
	;2000:0的数据放入al中
	mov bx,0 ;
	mov al,[bx] 
	
	;2000:1的数据放入bl中
	mov bx,1
	mov bl,[bx]
	
	;2000:2的数据放入cl中
	mov cl,ds:[2]
	
	;2000:3的数据放入dl中
	mov dl,ds:[3]
	
	mov ax,4c00H
	int 21h
codesg ends

end start

注意区别以下指令

mov al,[0] ;等价于mov al,0,ax赋值为0

mov al,ds:[0] ;将物理地址的内容数据放入ax

mov al,[bx] ;将物理地址的内容数据放入ax

mov al,ds:[bx] ;将物理地址的内容数据放入ax

loop指令:

  • cx的值表示循环次数,每次循环cx–,当cx不为零时进入循环
  • 循环s标签的代码段

实例:计算2^12

;debug命令:
;ml 1.asm ,debug 1.exe,p运行到程序结尾

assume cs:codesg

codesg segment

fishcc: 
	  mov ax,2
	  mov cx,11
	s:add ax,ax  ;循环体 
		loop s
			
	  mov ax,4c00H
	  int 21HsS
codesg ends

end fishcc

实现数据相加并放入dx中

涉及到内存地址的数据为8位,而dx是16位,不能直接放入dx中,而要通过al间接放入.

  • 将数据放入ax的低位al中,ah置为0
  • 再将ax加入dx中,实现同类型相加
  • 循环12次

;将 ffff:0~ffff:b 数据之和放入dx

assume cs:codesg

codesg segment

start: 
	mov ax,0ffffh
	mov ds,ax   ;不能直接mov ds,0ffffh!
	mov bx,0  ;bx作为偏移地址的变量
	mov dx,0 ;初始化dx值
	mov cx,12 ;循环次数
	
  s:mov al,ds:[bx]
	mov ah,0
	add dx,ax
	inc bx
	
	loop s
			
	mov ax,4c00H
	int 21H
codesg ends

end start

多段程序

  • 数据段 ds:data segment
  • 栈段 ss:stack segment
  • 代码段 cs:code segment

将8个字型数据逆序存放

;告知CPU cs存放code段,ds存放data段,ss存放stack段
assume cs:code,ds:data,ss:stack

;数据段
data segment
	dw 1h,2h,3h,4h,5h,6h,7h,8h 
data ends

;栈段
stack segment
	dw 0,0,0,0,0,0,0,0 
stack ends

;代码段
code segment
start:
	mov ax,stack	
	mov ss,ax
	mov sp,16	 	;栈顶指向 stack:16
	
	mov ax,data
	mov ds,ax 		
	
	mov bx,0 		;ds:0开始存放数据
	mov cx,8 		;循环次数
	s:
		push ds:[bx]  ;将数据段的内容入栈
		add bx,2
		loop s

		mov bx,0
		mov cx,8
	s0:
		pop ds:[bx]  ;将数据段的内容出栈
		add bx,2
		loop s0
		
		mov ax,4c00h
		int 21h
code ends
end start

将a段和b段的数据依次相加,保存到sum段

assume cs:codesg

a segment
	db 1,2,3,4,5,6,7,8
a ends

b segment
	db 1,2,3,4,5,6,7,8
b ends

sum segment
	db 0,0,0,0,0,0,0,0
sum	ends

codesg segment
start:
	mov ax,a	;a存放在ds:[0]
	mov ds,ax
	
	mov ax,b	;b存放在es:[0]
	mov es,ax
	
	mov cx,8
	mov bx,0
 s: 
	mov al,es:[bx]
	add ds:[bx],al	;ds:[0]+es:[0]的和保存在ds:[0]
	inc bx
	loop s
	
	mov ax,sum
	mov es,ax	;覆盖b,将sum保存在es:[0]

	mov bx,0
	mov cx,8
 s0:
	mov ax,ds:[bx]
	mov es:[bx],ax	;将ds:[0]的内容转移到es:[0]
	add bx,2
	loop s0
	
	mov ax,4c00h
	int 21h

codesg ends
end start

定位内存地址

大小写字母转化:

A的二进制吧表示为: 01000001
a的二进制吧表示为: 01100001

可以看到大写字母的二进制表示从低到高第6位为0,而小写字母为1.

因此:
大写转小写只需将第6位置为1,与00100000进行与运算.

小写转大写只需将第6位置为0,与11011111进行或运算.

法一:单独指定每个字符串的首地址

; 大小写转化
; 大写字母二进制表示从低到高第6位为0,小写字母为1

; 小写转大写:
;6位置为0:and 11011111

; 大写转小写
;6位置为1:or 00100000

assume cs:codesg,ds:datasg

datasg segment  ;数据段为两个字符串
	db 'BaSiC'
	db 'INFOR'
datasg ends

codesg segment
start:	
	mov ax,datasg
	mov ds,ax 	;数据存放在ds:[bx]
		
	mov bx,0
	mov cx,5	;第一个字符串长度
	
	s:mov al,[bx]
	  and al,11011111b ;6位置为0,变为大写
	  mov [bx],al
	  
	  inc bx
	  loop s
	
	mov cx,11	;第二个字符串长度
	mov bx,5	;第二个字符串地址起点
	
	s1:mov al,[bx] 
	   or al,00100000b ;6位置为1,变为小写
	   mov [bx],al
	   
	   inc bx
	   loop s1
		
	mov ax,4c00h
	int 21h
codesg ends

end start	

法二:
在两个字符串等长的情况下,可以利用 [字符串起始地址+bx]同时指定两个字符串的首地址

; 大小写转化
; 一个循环实现

assume cs:codesg,ds:datasg

datasg segment
	db 'BaSiC'
	db 'INFOR'
datasg ends

codesg segment
start:	
	mov ax,datasg
	mov ds,ax 	;数据存放在ds:[bx]
		
	mov bx,0
	mov cx,5
	
	s:mov al,[bx]	;第一个字符串从下标0开始
	  and al,11011111b 
	  mov [bx],al
	  
	  mov al,[5+bx] ;第二个字符串从下标5开始
      or al,00100000b 
      mov [5+bx],al
	  
	  inc bx
	  loop s
	
	mov ax,4c00h
	int 21h
codesg ends

end start	

二维字符串的大写转化:

;将下面四行的每个字母转化为大写,每一行占16个字节

assume cs:codesg,ds:datasg,ss:stacksg

datasg segment
	db 'abc             '	
	db 'def             '
	db 'ghi             '
	db 'jkl  			'
datasg ends

stacksg segment
	db 0,0,0,0,0,0,0,0
stacksg ends

codesg segment
start:
	mov ax,stacksg	;栈段
	mov ss,ax
	mov sp,16	

	mov ax,datasg
	mov ds,ax	;数据段
	mov bx,0
	
	mov cx,4	;外层循环次数
s0: push cx		;保存外层循环次数
	mov si,0	;初始化列的偏移量
	
	mov cx,3	;内层循环次数	 
 s: mov al,[bx+si]
    and al,11011111b		;转为大写
    mov [bx+si],al
    inc si				;下一列
    loop s
	
	add bx,16	;定位到下一行
	pop cx		;还原外层循环次数的值
	loop s0
	
	mov ax,4c00h
    int 21h
	
codesg ends

end start

数据处理的两个基本问题

数据存储在什么地方

  • 8086CPU中,只有四个寄存器(bx,bp,si,di)可以用 [] 进行内存单元的寻址.

  • bx,bp,si,di可以进行组合,但是**[bx+bp],[si+di]**是非法的

  • 寄存器bp类似与sp,段地址也是默认存放在ss中
    -可以用[bx+idata+si]的方式访问结构体中的数据
    bx访问整个结构体,idata指定结构体中的某一数据项,si指定数组项的每个元素
    例如:mov [bx].10h[si],‘V’ 等价于C语言中的 structName.obj[i]=‘V’

指令要处理的数据有多长:

  • word ptr指定一个字单元
    mov word ptr ds:[0],1,内存中为 01h 00h

  • dword指定两个字单元
    mov dword ptr ds:[0],1 内存中为 00h 00h 00h 01h

  • byte ptr指定一个字节单元
    mov byte ptr ds:[0],1 ,内存为 01h

  • push pop指令只执行字操作,操作数为一个字节

寻址方式在结构化数据访问的应用

题目:
在这里插入图片描述

在这里插入图片描述

assume cs:code,ds:data,ss:stack
 
data segment
  ;年份 每个占4个字节,年份的地址范围是0~83,0~53h
  db '1975','1976','1977','1978','1979','1980','1981','1982','1983','1984'
  db '1985','1986','1987','1988','1989','1990','1991','1992','1993','1994','1995'
  
  ;收入 每个数据占4个字节,收入的地址范围是 84~168,54h~A7h
  dd 16,22,382,1356,2390,8000,16000,24486,50065,97479,140417,197514
  dd 345980,590827,803530,1183000,1843000,2759000,3753000,4649000,5937000
  
  ;雇员数 每个数据占两个字节,169~210,A8h~D1h
  dw 3,7,9,13,28,38,130,220,476,778,1001,1442,2258,2793,4037,5635,8226
  dw 11542,14430,15257,17800
data ends
 
 
stack segment
	db 0
stack ends


table segment
  db 21 dup ('year summ ne ?? ')
table ends

code segment
start:
	mov ax,data	;数据段
	mov ds,ax
	
	mov ax,stack  ;栈段
	mov ss,ax
	mov sp,10h	;ss:sp指向站定
	
	add ax,1h	;目的地址es:[]
	mov es,ax	;es指向table段
	
	
	;1.复制年份
	mov bx,0	;行索引,从第一行开始
	mov di,0	;原始数据列标记
	mov cx,21
s1:	
	push cx 	;记录外层循环次数
	mov cx,4	;内层循环次数,年份4个字节
	mov si,0	;目标数据列标记	
	s2:	
		mov al,ds:[di]
		mov es:[bx+si],al	;bx索引行,si索引到该行的数据项的所有列
		inc di
		inc si
		loop s2
	add bx,10h ;下一行
	pop cx
	loop s1
	
		
	;2.复制收入
	mov bx,0	;行索引,从第一行开始
	mov cx,21
s3:	
	push cx 	;记录外层循环次数
	mov cx,4	;内层循环次数,收入两个字节
	mov si,5	;目标数据列标记
	s4:	
		mov al,ds:[di]
		mov es:[bx+si],al
		inc di
		inc si
		loop s4
	add bx,10h ;下一行
	pop cx
	loop s3
	
	
	
	;3.复制雇员数
	mov bx,0	;行索引,从第一行开始
	mov cx,21
s5:	
	push cx 	;记录外层循环次数
	mov cx,2	;内层循环次数,雇员数两个字节
	mov si,0ah	;目标数据列标记
	s6:	
		mov al,ds:[di]
		mov es:[bx+si],al
		inc di
		inc si
		loop s6
	add bx,10h ;下一行
	pop cx
	loop s5
	
	
	;4.计算人均收入
	mov si,0dh			;数据项所在的列的起始下标,年收入:[05],雇员数:[0a],人均收入:[0dh]
	mov bx,0			;行索引
	mov cx,21			;循环次数
s7:
	mov ax,es:[bx+5]	;年收入的低位存放到ax
	mov dx,es:[bx+7]	;年收入的高位存放到dx
	div word ptr es:[bx+0ah]	;除以人数,人数保存在[0ah]列
	mov es:[bx+si],ax		;商保存在ax中
	add bx,10h			;下一行
	loop s7
	
	mov ax,4c00H          ;程序返回
    int 21H

code ends

end start

 

转移指令

jmp指令:

  • jmp short 标号 (段内转移)
  • jmp far 标号 (段间转移)
  • jmp word ptr 地址 (段内转移)
  • jmp dword ptr 地址 (段间转移)

跳转到 cs:ip=0000:0123h

mov ax,0123h
mov ds:[0],ax 	;内存单元数据为: 0123h
mov word ptr ds:[2],0	;两个字节覆盖00000123h 
jmp dword ptr ds:[0]    ;cs=0000h,ip=0123h

call 和 ret 指令

  • call指令:将cs,ip入栈,并跳转至标号所在的语句
  • ret: 出栈给cs,ip赋值,返回到call指令的下一条语句

call ret基础应用

assume cs:code

code segment
start:
	mov ax,1
	mov cx,3
	call s	;跳转到s标号处执行
	
	mov bx,ax	;bx=ax=8
	mov ax,4c00h
	int 21h
	
	s:
		add ax,ax
		loop s	;执行3次循环后,ax=8

		ret	;跳转到从s下一句执行
code ends

end start

将dw段每个数的三次方依次存放到dd段


assume cs:code,ds:data

data segment
   	dw 1,2,3,4,5,6,7,8
	dd 0,0,0,0,0,0,0,0
data ends

code segment
start:
	mov ax,data
	mov ds,ax
	
	mov si,0	;原数据下标起始	
	mov di,16	;目的地下标起始
	
	mov cx,8
 s:	
	mov bx,[si]		;将原数据放入bx
	call cube		;调用函数cube
	
	mov [di],ax	    ;结果的低位
	mov [di+2],dx 	;结果的高位
	
	add si,2		;移动到下一个数据
	add di,4
	
	loop s
	
	mov ax,4c00h
	int 21h
	
cube:	;计算3次方
	mov ax,bx
	mul bx
	mul bx
	ret 	;函数返回
	
code ends
end start

大写转化

assume cs:code,ds:data

data segment
	db 'conversation'
data ends

code segment
start:
	mov ax,data
	mov ds,ax
	mov si,0
	
	call capital	;函数调用
	
	mov ax,4c00h
	int 21h

  capital:	;大写转化
	mov cx,12
	and  byte ptr [si],11011111b
	inc si
	loop capital
	ret		;函数返回
	
code ends
end start

标志寄存器

  • SF: 负数 SF=1 ,正数 SF=0
  • ZF: 为0 ZF=1 ,不为0 ZF=0
  • CF:无符号数运算时,产生进位或者借位 CF=CY,否则CF=NC
  • OF:有符号数运算时,产生溢出 OF=OV,否则OF=NV

adc指令:
带进位的加法指令 : adc ax,bx等价于 ax+bx+CF

adc48位加法:

; adc指令计算1ef0001000h + 2010001ef0h
assume cs:code

code segment
start:
	mov cx,1000h	;cx存放低16位
	mov bx,0f000h	;bx存放次高16位
	mov ax,001eh	;ax存放高16位
	
	add cx,1ef0h	;16位相加
	adc bx,1000h	;次高16位进位加
	adc ax,0020h	;16位进位加
	
	mov ax,4c00h
	int 21h
code ends
end start

adc128位加法

;adc 128位加法
;需要128 16个字节 8个内存单元 
assume cs:code,ds:data

data segment
	db 88h,88h,88h,88h,88h,88h,88h,88h,88h,88h,88h,88h,88h,88h,88h,88h
	db 11h,11h,11h,11h,11h,11h,11h,11h,11h,11h,11h,11h,11h,11h,11h,11h
data ends

code segment
start:
	mov ax,data
	mov ds,ax
	
	mov si,0	;第一个数据的起始
	mov di,16	;第二个数据的起始
	
	call add128	;函数调用
	
	mov ax,4c00h
	int 21h
	
add128:			;计算8个内存单元的和
	push ax		
	push si
	push di
	
	sub ax,ax	;将CF进位置为0
	
	mov cx,8	;循环8次
	
 s:	mov ax,[si]	;每次计算一个内存单元的和
	adc ax,[di]
	mov [si],ax ;放回结果存放回第一个数据的内存单元
	inc si		;移动到下一内存单元
	inc si
	inc di
	inc di
	loop s
	
	pop di
	pop si
	pop ax
	
	ret
	
code ends
end start

cmp指令

cmp 指令不记录结果,仅仅用于改变各个标志位

cmp ax,bx 如果ax=bx,则设置ZF=0

  • 在进行无符号的数的比较时,ZF,SF用来判断结果
  • 在进行有符号的数的比较时,还需要通过OF检查是否溢出,如果OF=1,则产生溢出,对SF取反才是正确结果

cmp a,b

je s : a,b 相等时跳转到 s

ja s : a>b 时跳转到s

jna s: a<b 时跳转到s

;统计data段中8的个数

assume cs:code,ds:data

data segment
	db 8,11,8,1,8,5,63,38
data ends

code segment

start:
	mov ax,data
	mov ds,ax
	
	mov ax,0
	mov bx,0
	mov cx,8
	
 s: cmp byte ptr [bx],8
	je ok			;相等时跳转到ok
	jmp short next	;不相等跳转到next
	
 ok:inc ax		    ;相等计数器+1
	
 next:
	inc bx
	loop s
	
	mov ax,4c00h
	int 21h

code ends
end start

movsb指令

实现将ds:[si]的内容复制到es:[di]

DF=0:si,di递增的方向,通过cld设置

DF=1:si,di递减的方向,通过std设置

正向传送:

;data段中的第一行复制给第二行

assume cs:code,ds:data

data segment
	db 'Welcome to masm!'
	db 16 dup(0)
data ends

code segment

start:
	mov ax,data
	
	mov ds,ax		;原数据段地址
	mov es,ax		;目的地段地址
	
	mov si,0		;原数据偏移地址	
	mov di,16		;目的地偏移地址
	
	mov cx,16		;处理16个字节
	
	cld				;设置DF=0,正向传送,si,di递增的方向	
	
	rep movsb		;将ds:[si]的内容复制到es:[di]
	
	mov ax,4c00h
	int 21h

code ends
end start

逆向传送:

;0f000:ffff中最后16个字节复制到data段中

assume cs:code,ds:data

data segment
	db 16 dup(0)
data ends

code segment

start:
	mov ax,0f000h
	mov ds,ax		;原数据段地址
	
	mov ax,data
	mov es,ax		;目的地段地址
	
	mov si,0ffffh	;原数据偏移地址	
	mov di,15		;目的地偏移地址
	mov cx,16		;处理16个字节
	
	std				;设置DF=1,逆向传送,si,di递减的方向	
	
	rep movsb		;将ds:[si]的内容复制到es:[di]
	
	mov ax,4c00h
	int 21h

code ends
end start

中断

  • 外部中断:由计算机外设发出的中断请求,如键盘中断,可以屏蔽的。
  • 内部中断:硬件或运算出错导致的中断,不可屏蔽。

8086CPU处理中断的过程:

  1. 获取中断类型码n
  2. 保存标志寄存器的值
  3. 保存cs,ip的值
  4. 从地址 n4+2:n4 处读取中断程序入口地址,设置cs:ip

中断程序7ch的安装: 调用int 7ch时,实现ax*ax

  1. 将程序sqr安装在0:200处
  2. 将0:200放入中断注册表
;中断程序7ch的安装

;中断程序7ch完成ax的平方计算

assume cs:code

code segment
start:
	mov ax,cs	
	mov ds,ax	;源段地址
	mov si,offset sqr	;源偏移地址
	
	mov ax,0
	mov es,ax	;目的地段地址
	mov di,200h	;目的地偏移地址
	
	;安装程序sqr到0:200
	mov cx,offset sqrend-offset sqr	;程序长度
	cld
	rep movsb 		
	
	;7ch中断放入中断向量表中
	mov ax,0
	mov es,ax
	mov word ptr es:[7ch*4],200h	;复制偏移地址
	mov word ptr es:[7ch*4+2],0h	;复制段地址
	
	mov ax,4c00h
    int 21h
  
  sqr:
	mul ax
	iret	;恢复标志寄存器,cs,ip
  sqrend:
	nop
	
code ends
end start

特殊中断:
ss:sp联合指向栈顶,因此要连续设置ss,sp

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值