先贴上程序
首先要知道cpu是不知道到底哪里的数据是代码 那里是数据那里是堆栈因此实现这些要靠程序员来告诉CPU
当然还有小小的汇编程序:汇编中的嵌套循环
assume cs:code,ds:data,ss:stack
data segment
dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h
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, 20h
mov ax,data
mov ds,ax
mov bx, 0
mov cx,8
s: push [bx]
add bx, 2
loop s
mov bx, 0
mov cx,8
s0: pop [bx]
add bx,2
loop s0
mov ax, 4c00H
int 21H
code ends
end start ;end指定 start是代码段的开始
;程序的开始是由end指定的,end后面跟的什么程序的入口就会在那里
下面是程序的分段详解
开头
assume cs:code,ds:data,ss:stack
假设程序分别有 代码段 数据段 堆栈段
assume 假设
声明一个数据段
data segment
dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h
data ends
声明一个堆栈段
stack segment
dw 0,0,0,0,0,0,0,0
stack ends
声明一个代码段
code segment ;代码段的开始
start: mov ax, stack ;程序的 ip指针会指向 start初
;因为8086cpu不允许将数据直接送入段寄存器中因此使用通用寄存器进行
mov ss, ax ;也就是相当于将stack中的数据送入到ss中
mov sp, 20h ;因为数据和堆栈占用了32个字节的大小一次sp堆栈的指针指向32处
mov ax,data ;通过寄存器ax将数据段的地址赋值给ds即段寄存器
mov ds,ax
mov bx, 0
mov cx,8 ;在循环的时候cx就是循环的计数器 每次循环就会将cx的数值进行减一直到为零为止
s: push [bx] ;该语句说的是将ds中的数据进栈也就相当于使用push ds:[bx] (说明push 是对堆栈的操作也就是push和pop会对堆栈进行操作)具体见文下注释;实现的过程就是将数据段中的数据压入堆栈 ,也就是对ss:sp指向的数据进行操作
add bx, 2
loop s //跳到s处进行循环
mov bx, 0
mov cx,8
s0: pop [bx]
add bx,2
loop s0
mov ax, 4c00H ;加上下一句实现程序的返回
int 21H
code ends ;代码段结束
end start //整段程序结束
注释:push-pop
堆栈操作指令 PUSH和POP
格式: PUSH OPRD
---- POP OPRD
功能: 实现压入操作的指令是PUSH指令;实现弹出操作的指令是POP指令.
----
说明: 1. OPRD为16位(字)操作数,可以是寄存器或存储器操作数.
----
2. PUSH的操作过程是: (SP)<--(SP)-2,((SP))<--OPRD 即先修改堆栈指针SP(压入时为自动减2),然后,将指定的操作数送入新的栈顶位置.
此处的((SP))<--OPRD,也可以理解为:
[(SS)*16+(SP)]<--OPRD 或 [SS:SP]<--OPRD
3. 示例: PUSH DX
PUSH BP
PUSH CS
PUSH DATA1
PUSH ALFA[BX][SI]
注意: 每进行一次压入操作,都压入一个字(16位).
4. PUSH和POP指令对状态标志位没有影响。
5. 什么是堆栈
堆栈被定义为一种先进后出的数据结构,即最后进栈的元素将被最先弹出来.这很像许多人进入一条窄得只能容纳一个人通过的小道,如果要从这条道往回退出 来的话,那么最先退出来的人是最后一个进入小道的人.
知识小百科:
数据传送指令 MOV
格式: MOV OPRD1,OPRD2
----
功能: 本指令将一个源操作数送到目的操作数中,即OPRD1<--OPRD2.
----
说明: 1. OPRD1 为目的操作数,可以是寄存器、存储器、累加器.
---- OPRD2 为源操作数,可以是寄存器、存储器、累加器和立即数.
2. MOV 指令以分为以下四种情况:
<1> 寄存器与寄存器之间的数据传送指令
示例: MOV AX,BX
MOV DS,AX
MOV BP,SI
注意: 代码段寄存器CS及指令指针IP不参加数的传送,其中CS可以作为
源操作数参加传送,但不能作为目的操作数参加传送.
<2> 立即数到通用寄存器数据传送指令
立即数只能作源操作数使用,不能作目的操作数.
示例: MOV AL,25
MOV SI,OFFSET DATA1
注意: 由于传送的数据可能是字节,也可能是字,源操作数与目的操作
数的类型应一致。
<3> 寄存器与存储器之间的数据传送指令
示例: MOV AL,BUFFER
MOV AX,[SI]
MOV LAST[BX+DI],DL
MOV SI,ES:[BP]
<4> 立即数到存储器的数据传送
示例: MOV ALFA,24
MOV DS:MEMS[BP],300AH
MOV BYTE PTR[SI],15
MOV LAST[BX][DX],0FFH
3. 本指令不影响状态标志位.
4. MOV指令执行时的数据传送方向.
<1> 立即数只能作为源操作数,不允许作目的操作数,立即数也不通送至段寄存 器.
<2> 通用寄存器可以与段寄存器,存储器互相传送数据,寄存器之间也可以互相 传送.但CS段不能作为目的操作数.
<3> 值得再次强调的是存储器与存储器之间不能进行数据直接传送.若要实现 存储单元间的数据传送,可以借助于通用寄存器作为中介来进行.
加法指令 ADD(Addition)
格式: ADD OPRD1,OPRD2
----
功能: 两数相加
----
说明: 1. OPRD1为任一通用寄存器或存储器操作数,可以是任意一个通用寄存器,而且还可 ---- 以是任意一个存储器操作数.这给程序的编写带来了很大的方便.
OPRD2为立即数,也可以是任意一个通用寄存器操作数.立即数只能用于源操作数
2. OPRD1和OPRD2均为寄存器是允许的,一个为寄存器而另一个为存储器也是允许的 ,但不允许两个都是存储器操作数.理由是指令代码的寻址方式中规定了两个操 作数(除立即数)至少有一个是寄存器操作数.
3. 加法指令运算的结果对CF、SF、OF、PF、ZF、AF都会有影响.以上标志也称为结 果标志.加法指令适用于无符号数或有符号数的加法运算.操作数可以是8位,也 可以是16位.
4. 示例: ADD AL,25 ;(AL)<--(AL)+25
ADD BX,0A0AH ;(BX)<--(BX)+0A0AH
ADD DX,DATA1[BX]
ADD DI,CX ;(DI)<--(DI)+(CX)
ADD BETA[BX],AX
ADD BYTE PTR[BX],28
注意: 上述第一条指令及第六条指令为字节相加指令,其它四条均为字(双字节) 相加指令.
未加注解的三条指,都与存储器操作数有关.第三条指指令中,存储器操作 数是源操作数,采用变址寻址方式;第五条指令中,存储器操作数是目的操 作数,采用变址寻址方式;第六条指令中存储器操作数也是目的操作数,采 用寄存器间接寻址方式,当立即数与存储器操作数做加法时, 类型必须一 致,故此处用BYTE PTR[BX],将存储器操作数的类型重新指定为字节类型, 以保证两个操作数类型一致.
数据传送指令 MOV
格式: MOV OPRD1,OPRD2
----
功能: 本指令将一个源操作数送到目的操作数中,即OPRD1<--OPRD2.
----
说明: 1. OPRD1 为目的操作数,可以是寄存器、存储器、累加器.
---- OPRD2 为源操作数,可以是寄存器、存储器、累加器和立即数.
2. MOV 指令以分为以下四种情况:
<1> 寄存器与寄存器之间的数据传送指令
示例: MOV AX,BX
MOV DS,AX
MOV BP,SI
注意: 代码段寄存器CS及指令指针IP不参加数的传送,其中CS可以作为
源操作数参加传送,但不能作为目的操作数参加传送.
<2> 立即数到通用寄存器数据传送指令
立即数只能作源操作数使用,不能作目的操作数.
示例: MOV AL,25
MOV SI,OFFSET DATA1
注意: 由于传送的数据可能是字节,也可能是字,源操作数与目的操作
数的类型应一致。
<3> 寄存器与存储器之间的数据传送指令
示例: MOV AL,BUFFER
MOV AX,[SI]
MOV LAST[BX+DI],DL
MOV SI,ES:[BP]
<4> 立即数到存储器的数据传送
示例: MOV ALFA,24
MOV DS:MEMS[BP],300AH
MOV BYTE PTR[SI],15
MOV LAST[BX][DX],0FFH
3. 本指令不影响状态标志位.
4. MOV指令执行时的数据传送方向.
<1> 立即数只能作为源操作数,不允许作目的操作数,立即数也不通送至段寄存 器.
<2> 通用寄存器可以与段寄存器,存储器互相传送数据,寄存器之间也可以互相 传送.但CS段不能作为目的操作数.
<3> 值得再次强调的是存储器与存储器之间不能进行数据直接传送.若要实现 存储单元间的数据传送,可以借助于通用寄存器作为中介来进行.
段定义伪操作 SEGMENT和ENDS
格式: <段名>SEGMENT[<定位类型>],[<组合类型>],[<'类别名'>]
---- …:段体
<段名>ENDS
功能: SEGMENT和ENDS伪操作命令可用来把程序分成若干逻辑段,这些逻辑段按用途不同 ---- ,通常包括代码段、数据段、堆栈和附加段,它分别装入由CS、DS、SS和ES寄存器 所指定的物理段中。
说明: 1. 定位类型
---- 定位类型表示某段装入内存时,对段的起始边界有何要求,有以下四种选择:
BYTE:表示本段起始单元可以从任一地址开始,段间不留空隙,存储器利用率最 高。
WORD:表示本段起始单元从一个偶字节地址开始,即段起始地址的最后一位二进 制数一定是0,如02152H、0A156H等。这种定位方式适合于数据项的类型 为字的数据段。
PARA:这是隐含选择,表示本段起始地址从一个字节的边界开始。一个节为16个 字节,所以段的起始地址一定能被16整除,亦即是以0H结尾的地址,如 024D0H、07A20H等。
PAGE:表示本段起始地址从一个页的边界开始。一页为256个字节,所以段的起 始地址一定能被256整除,以即是以00H结尾的地址,如02400H、07A00H等
2. 组合类型(Combine Type)
NONE:这是隐含选择,表示本段与其他段无组合关系,每段都有自己的段基址
PUBLIC:表示在满足定位类型的前提下与其他模块的同名段邻接在一起,形成 一个新的逻辑段,公用一个段基址,所有偏移量调整为相对于新逻辑 段的起始地址。
COMMON:表示产生一个覆盖段。也就是说,当一两个段连接时,把本段与其他 也用COMMON说明的同名段置成相同的起始地址,共享相同的存储区。 共享存储区的长度由其中最大的段确定。
STACK:表示把所有同名段连接成一个连续段,自动初始化SS和SP,使SS 的内 容为该连续段的首地址,SP指向堆栈底部+1的存储单元。
MEMORY:表示本段在存储器中应定位在所有其他段的最高地址。
3. 类别名(Class) 类别名可以是任何合法的名字,必须用单引号括起来。
模块定义伪操作 NAME和END
格式: [NAME<模块名>]
---- ...
END[标号]
功能: 定义一个模块
说明: 模块命名伪操作命令NAME可以省略。这时,该模块的命名规则是:如果模块中使 ---- 用了TITLE语句(列表输出的页标题命令),则TITLE语句中页标题的前六个字符 就是模块名;如果模块中没有使用TITLE语句,则该模块的源程序文件名就是模块 名。
加法指令 ADD(Addition)
格式: ADD OPRD1,OPRD2
----
功能: 两数相加
----
说明: 1. OPRD1为任一通用寄存器或存储器操作数,可以是任意一个通用寄存器,而且还可 ---- 以是任意一个存储器操作数.这给程序的编写带来了很大的方便.
OPRD2为立即数,也可以是任意一个通用寄存器操作数.立即数只能用于源操作数
2. OPRD1和OPRD2均为寄存器是允许的,一个为寄存器而另一个为存储器也是允许的 ,但不允许两个都是存储器操作数.理由是指令代码的寻址方式中规定了两个操 作数(除立即数)至少有一个是寄存器操作数.
3. 加法指令运算的结果对CF、SF、OF、PF、ZF、AF都会有影响.以上标志也称为结 果标志.加法指令适用于无符号数或有符号数的加法运算.操作数可以是8位,也 可以是16位.
4. 示例: ADD AL,25 ;(AL)<--(AL)+25
ADD BX,0A0AH ;(BX)<--(BX)+0A0AH
ADD DX,DATA1[BX]
ADD DI,CX ;(DI)<--(DI)+(CX)
ADD BETA[BX],AX
ADD BYTE PTR[BX],28
注意: 上述第一条指令及第六条指令为字节相加指令,其它四条均为字(双字节) 相加指令.
未加注解的三条指,都与存储器操作数有关.第三条指指令中,存储器操作 数是源操作数,采用变址寻址方式;第五条指令中,存储器操作数是目的操 作数,采用变址寻址方式;第六条指令中存储器操作数也是目的操作数,采 用寄存器间接寻址方式,当立即数与存储器操作数做加法时, 类型必须一 致,故此处用BYTE PTR[BX],将存储器操作数的类型重新指定为字节类型, 以保证两个操作数类型一致.