基础知识
汇编语言由汇编指令,伪指令,其他符号组成, 其中核心是汇编指令,也可以称之为机器码的助记符。
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处理中断的过程:
- 获取中断类型码n
- 保存标志寄存器的值
- 保存cs,ip的值
- 从地址 n4+2:n4 处读取中断程序入口地址,设置cs:ip
中断程序7ch的安装: 调用int 7ch时,实现ax*ax
- 将程序sqr安装在0:200处
- 将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
763

被折叠的 条评论
为什么被折叠?



