准考证打印入口

微型计算机原理及应用学习笔记 汇编语言程序设

发布日期:2018-05-23 12:28:38 编辑整理:江西自考网 【字体: 】   【自考招生老师微信】
立即购买

《自考视频课程》名师讲解,轻松易懂,助您轻松上岸!低至199元/科!

一、概述
    单一的指令其功能比较简单,要解决一个实际问题总是需要一组指令按一定的思路有    机地组合起来才能完成,这样一组完成特定功能的指令有序集合称为程序。
  (一)程序设计的步骤
  从具体问题到编好程序要经过如下基本步骤: 
(1)  分析课题——弄清问题的性质、目的,已知数据,运算精度以及速度等方面的要    求。
(2)确定算法——把实际问题转化为计算机求解的步骤和方法,即算法,而程序是用来描述算法的。
  (3)画流程图——流程图是算法的一种直观而形象的表示方法,是对程序执行过程的    一种形象化的描述,又称为框图。 
  (4)编写程序——熟悉8086/8088的指令系统及程序设计常用技巧按流程图编写程    序。要求做到简单明了、层次清晰、运算迅速、少占内存。要编写高质量的汇编语言程序,必须加深对指令系统功能的理解,注意内存工作单元和工作寄存器的分配。  
  (5)上机调试、修改——可以通过单扳机或系统机进行调试、修改直至通过。
    (二)程序的基本结构  
    程序的基本结构有四种:顺序结构,分支程序结构,循环程序结构,子程序结构。现     分节筒述于下。
    二、顺序结构程序
    顺序结构的程序又称简单程序,这种结构的程序是顺序执行的, 无分支,无转移,无循环,程序本身的逻辑很简单,它只依赖于计算机能够顺序执行指令(语句)的特点,只要语句安排的顺序正确即可。
    例1  内存中自TABLESQ开始的16个单元连续存放着自然数0到15的平方值,任
  给一数X(0≤X≤15)在XX单元中,查表求出X的平方值,将结果存入YY单元中。
    首先在数据段中建立平方表,表的首地址为TABLESO,然后用程序找到X2值在平方表中的位置,即计算表地址,表地址 = 表起始地址(TABLESQ)+X,据此可写出如下程序。
    DATA    SEGMENT
     TABLESQ  DB O,1,4,9,16,25,36,49,
             DB 64,81,100,121,144,169,225
     XX   DB  ?
     YY   DB  ?
    DATA    ENDS
    STACK    SEGMENT PARA STACK‘STACK’
     DB  50 DUP(?)
    STACK    ENDS
    CODE    SEGMENT
    ASSUME  CS:CODE,DS:DATA,SS:STACK
    START    PROC FAR   *   *
    BEGIN:  PUSH DS    *   *
    MOV,AX,0          *   *
    PUSH AX             *   *
    MOV  AX,DATA
    MOV  DS,AX
    MOV  BX,OFFSET TABLESQ
    MOV  AH,0
    MOV  AL,XX
    ADD  BX,AX
    MOV  AL,[BX]
    MOV  YY,AL
    RET               *   *
    START  ENDP       *   *
    CODE   ENDS
END BEGIN
其中带 *   * 的六行语句的作用是为了在用户程序运行结束后,正确返回操作系统。    汇编语言源程序经过汇编、连接后生成 .EXE文件,可在DOS下直接键入文件名运行,例如命令 A>EXAMPLE 将把带有.EXE的文件EXAMPLE装入内存,并从程序中指定地址开始运行。装入文件并设置启动地址是由操作系统的COMMAND.COM文件完成的。COMMAND.COM文件在装入.EXE文件前,首先确定最低可用地址作为被装入程序的可用内存起点(该区段称为程序段)并在程序内从段内偏移地址处开辟100H字节的程序段前缀(PSP),在该PSP的前两字节存放一条 INT 20H 指令(即退出当前程序返回操作系统)。而COMMAND.COM把.EXE文件装入后,自动设置DS和ES寄存器指向程序段前缀,即此时的DS和ES值为程序段前缀之段基值。用户程序结束为RET,为使RET后程序转向程序段前缀中INT 20H指令,必须使CS为程序段前缀的段基值,IP为INT 20H的段内偏移值00H,因此必须把用户程序设计为过程,即加下START PROC FAR,在用户程序的前三条指令写入PUSH DS,MOV AX,0和PUSH AX,把DS = 程序段前缀之段基值,AX = 00H进栈,在用户程序最后用RET指令出栈到CS和IP,以保证程序转向INT 20H,进而返回操作系统。
    代码段结束的处理还可用DOS系统功能调用,在结尾处安排如下两条指令
    MOV AH、4CH
    INT 21H
    执行至此,返回调用程序,常用于应用DEBUG进行程序调试,可返回DEBUG。这 时,可不用把用户程序设计为过程,即去除带 *   * 的六行语句。
    例2  把非压缩十进制数转换为压缩十进制数,被转换数存放在DATl开始的两个单    元,转换后的数存回DATl单元。
    程序如下:
    MOV AX,DATI   ;(AX)=0109H
    MOV CL,4      ;(CL)=4
    SAL AH,CL     ;(AH)=10H
    ROL AX,CL     ;(AX)=0091H
    ROL AL,CL     ;(AL)=19H
MOV BYTE PTR DATl,AL    ;(DATl)=19H
    ┅
    DATl DW 0109H
    三、分支结构程序
    在实际的程序设计中,始终是顺序执行的情况是很少的,大部分程序在执行过程中,总是要求计算机作出一些判断,井根据判断作出不同的处理,这就引出了分支程序的概念。
    (一)分支程序的二要素
    分支结构程序是具有判断和转移功能的程序。
    1.判断——根据运算结果的状态标志
    判断前一定要经过运算(能影响状态标志的运算),状态标志反映了运算结果的特性。     这些状态标志是:进位标志CF、奇偶标志PF、零标志ZF、符号标志SF以及溢出标志OF。
    2.转移 — 主要由条件转移指令来实现(也可用无条件转移指令JMP)
    在8086/8088的指令系统中,条件转移指令可分为两大类,一类是按单标志位来判断  的,如JAE,JC,JZ,JO,JS等;另一类是按多标志位来判断,如JGE,JG,JA等,详见表3-6所示。
    分支结构程序框图如图4—1所示。
    (二)利用比较转移指令实现分支
    这是实现分支的一种常用方法,用于比较、判断的指令是CMP(比较指令)、CMPS(串比较指令)以及SCAS(串搜索指令)等。转移指令已如前述。而分支的次数可由具体问题决定是单重分支还是多重分支。n次判断可形成n+l路分支。
    例3  符号函数的处理
    有一符号函数
              1,  当 X>O  (-128≤X≤127)
       Y =     O,  当 X=0
             -1, 当 X<O
    设给定值x存放于LY单元,函数Y值存放于YY单元,则按X的不同取值给Y赋值的程序如下:
      MOV AL.XX
      CMP AL,0
      JGE BIGR
      MOV AL,0FFH
      MOV YY,AL    ;X<0时,—1送入YY单元
      HLT
   BIGR:JE EQUL
      MOV AL,1
一    MOV YY,AL    ;X>O时,l送人YY单元
        HLT
     EQUL:MOV  YY,AL    ;X=O时,O送人YY单元
        HLT
    这是一个多重分支的符号函数赋值程序,其流程图见图4-2 所示。
    例4数据块传送程序
    要求把内存中某一区域的源数据块传送到另一区域。在编写该程序时,必须判别一下    源数据区同目的数据区之间有无重叠,以决定是增量传送还是减量传送。程序清单如下:
    DATA    SEGMENT
    STRG    DB l000 DUP(?)
    STGl    EQU STRG+7
    STG2    EQU STRG+25
    SIRSE   EQU 50
    DATA    ENDS
    STACK   SEGMENT PARA STACK‘STACK’
    STARN    DB l000 DUP(?)
    STACK    ENDS
    COSEG    SEGMENT
    ASSUME CS:COSEG,DS:DATA,ES:DATA,SS:STACK
    GOO  PROC  FAR
    BEGIN: PUSH DS
            MOV  AX,0
            PUSH AX
            MOV  AX,DATA
            MOV  DS,AX
            MOV  ES,AX
            MOV  CX,STRSE
    MOV  SI,OFFSET STGl
            MOV  DI,OFFSET STG2 
            CLD                   ;增量方式
PUSHI SI
ADD  SI,STRSE-1
            CMP  SI,DI
            POP  SI
            JB  OK
            STD                  ;减量方式传送
            ADD  DI,STRSE-1     ;指向数据块底部
            ADD  SI,STRSE-1
        OK:REP MOVSB            ;重复传送50个数据
              RET
GOO    ENDP
        COSEG    ENDS
          END BEGIN
    (三)利用跳转表实现分支
    1.跳转表的建立
    在内存的一个连续区中,连续存放一系列跳转地址、跳转指令或关键字,组成一个决
  定程序分支的跳转表。
     例5  某工厂有8种产品的加工程序RO到R7分别存放在以SBRO,SBRl,…,SBR7为首地址的内存区域中,这8个首地址的偏移量连续存放在以BASE为首地址跳转表内,如图4-3 所示。
2.跳转表的使用
上例中,8种产品的编号为0,1,2,…,7 如果已知目前要加工的产品编号,要求编写程序,利用跳转表自动转入该产品的加工程序。
    在跳转表已知的情况下,关键问题要计算所要求的加工程序的入口地址在跳转表中的地址,即计算表地址。从图 4-3可见:
表地址=表基地址+偏移量
表基地址即跳转表的首地址,偏移量即对应的程序入口地址在表中的地址与表基地址
的距离。在本例中,分析表的结构可见,偏移量=产品编号 * 2,已知偏移量后,即可求出表地址。据此可写出[例5]的程序清单
    DATA    SEGMENT
    BASE  DW SBR0,SBRl,SBR2,SBR3
          DW SBR4,SBR5,SBR6,SBR7
    BN    DB ?
    DATA  ENDS
    STACK SEGMENT PARA STACK ‘STACK’
      DB  l00 DUP(?)
    STACK ENDS
    COSEG  SEGMENT
       ASSUME CS:COSEG,DS:DATA,SS:STACK
    START PROC FAR
    BEGIN:PUSH  DS
           MOV  AX,O
           PUSH  AX
           MOV  AX,DATA
           MOV  DS,AX
MOV  AL,BiN
                            MOV  AH.O
           ADD  AL,AL
           MOV  BX,OFFSETBASE
           ADD  BX,AX
           MOV  AX,[Bx]
           JMP  AX
           RET
           START  ENDP
           COSEG  ENDS
           END BEGIN 
    3.根据跳转表内指令分支
    在计算机系统的监控程序,键盘管理程序中经常要用到跳转表。在跳转表中既可存放    跳转地址(如例5),也可存放一系列的跳转指令JMP。
    例6  有一监控程序的键盘程序控制12个命令键——执行键(EXEC)、存储器检查键(MEM)等等,按下任一命令键相当于发出一条键盘命令,而这些命令的实现分别由监控程序中的12个于程序完成,这些子程序的入口地址分别为ADR0,ADRl,…,ADRll。据此可以  组成命令键跳转表如图4-4所示。该命令键跳转表存放在以BASE0为首地址的内存区域中,表内存放着12条转移指令:
    JMP  ADR0,JMP  ADRl,  …,JMP ADRll。
设12个命令键的编号分别为0 ~ 11。命令跳转表中每三个单元存放一条转移指令,如果命令键的编号X已送入寄存器AL,则实现转向相应的命令子程序的程序如下
    MOV  AH,0
    MOV  BL,AL
    ADD  AL,AL
ADD  AL,BL
ADD  AL, BL
    MOV  BX,OFFSET BASE0
    ADD  BX,AX          ;表地址计算
    JMP  BX
  
四、循环结构程序
    (一)概述
    1.循环程序
    实现重复执行某一段程序的程序结构称为循环程序。用来处理带重复性的问题,可以缩短源程序及目标程序。
    循环结构程序的框图之一如图4-5所示。
    循环程序由五部分组成:
    (1)初始化部分——这是循环的准备部分,为程序操作、地址指针、循环计数、结束条件等设置初始值。
   (2)循环工作部分——这是循环程序的主体,完成程序的基本操作,循环多少次,这部分的语句就执行多少次。
    (3)循环修改部分——修改循环工作部分的变量地址等,为下一轮重复操作作准备。
    (4)循环控制部分——修改计数器或判断循环结束条件以决定是继续循环还是终止循环,是典型的分支结构,不过继续
  循环的分支去向是循环体的头部。通常把循环工作部分称为循环体,循环体的第一条指令为循环体的头部。
    (5)循环结束部分——循环终止后,对循环结果的处理部分。
    2.循环程序的几种结构形式
    循环程序除图4-5所示的基本结构形式外,还有两种常用的结构形式,如图4-6和图
4-7所示。
    这两种结构的特点是工作部分放在循环控制部分之后,这样可以允许循环次数为零的    循环程序或不需要循环时不进入循环工作部分。
    3.循环程序分类
    (1)按循环控制条件分类
    1)计数循环——重复次数已知,用计数值控制循环的开始与终止。
    2)条件控制循环——重复次数未知或不确定,需找出循环控制的条件。
    (2)按循环体内结构分类
    1)单重循环——循环体内只是顺序程序或分支程序,不再有循环程序。
    2)多重循环——循环体内再套有循环程序。可把重复处理部分独立出来,前面加上    循环准备,后面加上结束判断。
    4.循环程序设计要点
    循环程序设计要点可归结为两点:
    (1)怎样把求解的问题变为循环结构的程序类型——怎样实现重复,即计算方案的循    环化。这里循环工作部分的设计尤为重要,因为这是多次重复的部分,注意程序的精练,及循环体头部的确定。
    (2)怎样使循环准确地执行完毕,这就要注意循环控制部分的设计。特别是对条件控    制的循环,注意设置循环结束标志。
    (二)循环程序举例
    例7计算Y=∑ai  , 0≤i≤100
有100个数a1,a2,…,a100,求这100个数之和(设和值不大于2个字节)。采用循环结构可写出如下程序:
DATA  SEGMENT
    TABL  DW a1,a2,…,a10
          DW all,a12,…,a20
           …      
          DW a91,a92,…,a100
    YY    DW ?
    DATA  ENDS
    STACK  SEGMENT PARA  STACK‘STACK’
          DB  l00 DUP(?)
    STACK  ENDS
    CODE  SEGMENT
      ASSUME CS:CODE,DS:DATA,SS:STACK
    GO  PROC FAR
    BEGIN:PUSH DS
           MOV AX,O
           PUSH AX
MOV AX,DATA
MOV DS, AX
MOV AX,0
MOV BX, OFFSET TABL  
MOV CX, 100
LOP:   ADD AX,[Bx]
INC BX
INC BX
DEC CX
JNZ LOP
MOV YY,  AX
RET
GO:    ENDP
CODE   ENDS
例8         统计数组中负元素的个数
数据段的定义如下:
DATA  SEGMENT
D1 DB -1,-3,5,6,-9,…
COUNT EQU $-D1
RS DW ?
DATA  ENDS
代码段程序为:
CODE SEGMENT
ASSUME CS:CODE,SS:STACK, DS:DATA
START PROC FAR
BEGING: PUSH DS
MOV AX,0
PUSH AX
MOV AX,DATA
MOV DS,AX
MOV BX,OFFSET D1     ;建立数据指针
MOV CX,COUNT         ;置计数器初值
MOV DX,0             ;置结果初值
LOP1: MOV AL,[BX]
CMP AL,0
JGE JUS
INC DX
JUS:  INC BX
DEC CX
JNZ LOP1
MOV RS,DX
RET
START ENDP
CODE ENDS
以上两例都是‘‘先执行,后判断”的结构。
 
    例9  寄存器AX中有一个16位二进制数,编程统计其中“1”的个数,结果存放在
CX中。
本例最好采用“先判断、后执行”的结构,即先检查凡x中有无为“1”的位。则代码段的有关程序如下:
MOV  CX, 0
LOP:  AND  AX, AX
JZ   STP
SAL  AX, 1
JNC  LOP
INC  CX
JMP  LOP
STP: HLT
例10  软件延时程序如下:
SOFTDLY PROC
MOV BL,10
DELAY: MOV CX, 2801
WAIT:  LOOP WAIT
DEC BL
JNZ DELAY
RET
 SOFTDLY ENDP
    这是一个二重循环结构,内循环即WAIT:LOOPWAIT,每个内循环可实现延时10ms左右,外循环人口为DELAY:MOV CX,2801,共进行10次,总的延时时间约lOOms。
    注意:可以从内循环直接跳到外循环,但不能从外循环直接跳进内循环。特别要注意  100循环体头部,防止死循环。
    五、子程序
    (一)概述
    1.子程序结构
    如果在一个程序中的多个地方或在多个程序中都要用到同一段程序,可以把该程序段    独立出来存放在内存的某一区域以供其他程序调用,这段程序称为子程序或过程。可见子    程序是可供其他程序调用的独立的、相对固定的程序段。调用子程序的程序体称为主程序    或调用程序。在实用中总把常用的子程序标准化后存放在一个内存区中,称为“子程序    库”。
    (1)结构。子程序的第一个语句前必须有“过程名”——入口地址的符号表示,出口是返回指令RET。
    (2)调用与返回。主程序通过书写调用指令CALL后跟子程序的入口地址来调用子    程序,8086/8088允许子程序在现行代码段中(子程序名为NEAR类型),也可以不在现行代码段中(子程序名为FAR类型)。为了保证正确返回,RET指令的类型,必须与CALL指令的类型相匹配。
    1)调用指令中的目标地址有两种表示方法:
    a.直接调用,目标地址就在指令中。例如:CALL NEAR-PRG  。
b.间接调用,目标地址在由指令指定的寄存器或内存单元中。例如:CALL DWORD PTR[BX]    对段内的直接调用而言,CALL指令首先将SP减2,使断点的IP栈,从指令中得到的目标过程的相对偏移量(最大不能越界)加到IP上。对段内的间接调用而言,以CALL AX为例,SP减2,IP进栈,AX→IP。
对段间的直接调用而言,SP减2,现行代码段寄存器CS的内容进栈,CS由指令中目标过程的段基值代入。SP再减2,IP进栈,然后将指令中的偏移地址代入IP。
    对段间的间接调用而言,SP减2,把现行的CS值进栈,CS由指令指定的双字存储器指针的第二个字的内容代入,SP再减2,IP进栈,然后IP由指令中指定的双字指针的第一个字的内容代入。双字存储器由数据段定义。
    2)返回指令RET也有两种情况:
    a. 段内返回指令,把SP所指的堆栈顶部的一个字的内容弹回IP,SP加2。
    b.段间返回指令,把SP所指的堆栈顶部的两个字的内容,先弹回IP,后弹回CS,SP加4。
    2.子程序文件
    子程序应以文件形式编写,子程序文件由子程序说明和子程序构成。
   (1)子程序说明。子程序说明包括如下部分:
    1)功能描述:包括子程序名称、功能,以及性能指标(如执行时间)等o
    2)所用寄存器和存储单元。
    3)子程序的入口、出口参数。
    4)子程序中又调用的其他子程序。
    5)调用实例(可无)。
 子程序说明举例如下:
    ;子程序PTOB
    ;两位十进制数(BCD码)转换成二进制数
    ;入口参数:AL中存放被转换数
    ;出口参数:CL中存放转换后的二进制数
    ;所用寄存器:BX
    ;执行时间:0.06ms
  (2)子程序。在8086/8088中,子程序本身常以过程形式存放在代码段中,以一个过    程名开始,以RET指令结束。
    例如: DTOB  PROC
           …
RET
           DTOB  ENDP
    3.子程序应用中应注意的问题
    (1)主程序与子程序的连接。主程序与子程序的连接由下例可见:
    例如;
    CODE  SEGMENT
      START:…  
          MOV AL,XX    ;被转换的数在XX单元
          PUSH BX
    CALL DTOB
    MOV YY,CL    ;转换结果存于YY单元
    POP BX
     …
    DTOB  PROC  
    RET
    DTOB    ENDP
    CODE    ENDS
    END START
    (2)子程序中所用寄存器及工作单元内容的保护。为了不破坏原有信息,对于在子程    序中要用到的某些寄存器和存储单元的内容,必须压入堆栈加以保护,也可存入一些空闲单元或某些目前不用的寄存器中。称为现场信息的保护。保护可以在子程序中实现,可在主程序中实现,一般在子程序中实现保护比较好。而对于用作中断服务的子程序,则一定在子程序中安排保护指令,因为中断的出现是随机的,无法在主程序中安排保护程序。
    (3)参数的传递。主程序与子程序通常需要交换信息,在多数情况下主程序需要向子  程序传递参数,子程序执行的结果要传送给主程序。子程序清单中的人口参数使子程序  对不同的数进行处理,出口参数可送出不同的结果。
     参数传递一般有三种方法:
  1)用寄存器传递,适用于参数较少的情况;
  2)用参数表传递,适用于参数较多的情况,要求事先建立参数表,参数表一般建立在内存中。
  3)用堆栈传递,适用于参数多并子程序有嵌套、递归调用的情况,主程序将参数压入堆栈,子程序中将参数从堆栈弹出。
4.子程序嵌套和子程序递归
    (1) 子程序嵌套 。子程序调用别的子程序,称为嵌套,。嵌套的层次只受空间的大小
的限制。
    (2)子程序递归。子程序直接或间接地调用子程序自身,称为递归。
    (二)子程序结构举例
    例11  数据段定义了两个数组ARYl和,ARY2,编一程序分别求此两数之和。主程序和过程分别安排在两个不同的段中,因此过程应是FAR类型。
    该程序清单如下
    STACK  SEGMENT PARA STACK
     DW 20 DUP(?)
STACK  ENDS
DATA  SEGMENT
ARY1 DB 100 DUP(?)
SUM1 DW ?
ARY2 DB 50 DUP(?)
SUM2 DW ?
DATA  ENDS
MAIN  SEGMENT          ;主程序段       
   ASSUME CS:MAIN, DS:DATA,SS:STACK
STR:PUSH DS
MOV AX,0
PUSH AX
MOV AX,DATA
MOV DS,AX
MOV AX,SIZE ARY1
PUSH AX           ;SUM过程的入口参数1 进栈
MOV AX,OFFSET ARY1
PUSH AX          
CALL SUM
   …
MOV AX,SIZE ARY2
PUSH AX           ;SUM过程的入口参数2 进栈
MOV AX,OFFSET ARY2
PUSH AX          
CALL SUM
HIT
MAIN  ENDS
 
 
 PROCE  SEGMENT     ; 过程段
        ASSUME  CS:PROCE,DS:DATA,SS:STACK
SUM  PROC FAR
PUSH AX
PUSH BX
PUSH CX
PUSH BP
N40V BP,SP
PUSHF
MOV CX,[BP+14]
MOV BX,[BP+12]
MOV AX,0
AND:  ADD AL,[BX]
INC BX
ADC AH,0
LOOP ADN
MOV[BX],AX     ;数组之和送到结果区
POPF            ;恢复现场
POP BP
POP CX
POP BX
POP AX
RET 4            ;返回并废除参数1和2
SUM  ENDP
PROCE  ENDS
END STR
 
从程序清单可见主程序向过程的参数传递是通过堆栈实现的,程序执行过程中堆栈变化如图所示。
    图4-8  例u中堆栈变化情况
 
程序中出现RET 4,该指令的功能是返回后,SP再加 4
例12  求n!
算法是
n! =  n(n-1)!   当n>0时;
  1         当n=0时
设数n存放在AL中,n! 存放在BX中。
;主程序
MAIN: MOV AX,3    ;设n=3
       CALL FACT
  X1: MOV BX,DX
       HLT
;阶乘子程序
;人口参数:AL中存放n
;出口参数:DX中存放n!
;所用寄存器:CX
FACT  PROC
CMP AL,0
JNZ IIA
MOV DL,1
RET               ;①
IIA: PUSH AX
DEC  AL
CALL FACT
X2:  POP CX
CALL MULT
X3:  MOV DX,AX
RET           ;②
FACT  ENDP
;无符号字节数乘法子程序
;入口参数:CL、DL中各为一乘数
;出口参数:AX中为乘积
MULT  PROC
… 
 RET     ;③
  MULT  ENDP
    该程序既包括子程序嵌套,也包括子程序递归。该子程序名为FACT,嵌套子程序即
FACT调用另一子程序MULT(无符号字节数乘法子程序),递归子程序即FACT调用FACT。注意三个RET指令的作用。

本文标签:江西自考 工学类 微型计算机原理及应用学习笔记 汇编语言程序设

转载请注明:文章转载自(http://www.zikao.jx.cn

本文地址:http://www.zikao.jx.cn/cj_gxl/8716.html



《江西自考网》免责声明:

1、由于各方面情况的调整与变化,本网提供的考试信息仅供参考,考试信息以省考试院及院校官方发布的信息为准。

2、本网信息来源为其他媒体的稿件转载,免费转载出于非商业性学习目的,版权归原作者所有,如有内容与版权问题等请与本站联系。联系邮箱:812379481@qq.com。

江西自考便捷服务

微型计算机原理及应用学习笔记 汇编语言程序设

日期:2018-05-23 12:28:38  整理:江西自考网  浏览(

一、概述
    单一的指令其功能比较简单,要解决一个实际问题总是需要一组指令按一定的思路有    机地组合起来才能完成,这样一组完成特定功能的指令有序集合称为程序。
  (一)程序设计的步骤
  从具体问题到编好程序要经过如下基本步骤: 
(1)  分析课题——弄清问题的性质、目的,已知数据,运算精度以及速度等方面的要    求。
(2)确定算法——把实际问题转化为计算机求解的步骤和方法,即算法,而程序是用来描述算法的。
  (3)画流程图——流程图是算法的一种直观而形象的表示方法,是对程序执行过程的    一种形象化的描述,又称为框图。 
  (4)编写程序——熟悉8086/8088的指令系统及程序设计常用技巧按流程图编写程    序。要求做到简单明了、层次清晰、运算迅速、少占内存。要编写高质量的汇编语言程序,必须加深对指令系统功能的理解,注意内存工作单元和工作寄存器的分配。  
  (5)上机调试、修改——可以通过单扳机或系统机进行调试、修改直至通过。
    (二)程序的基本结构  
    程序的基本结构有四种:顺序结构,分支程序结构,循环程序结构,子程序结构。现     分节筒述于下。
    二、顺序结构程序
    顺序结构的程序又称简单程序,这种结构的程序是顺序执行的, 无分支,无转移,无循环,程序本身的逻辑很简单,它只依赖于计算机能够顺序执行指令(语句)的特点,只要语句安排的顺序正确即可。
    例1  内存中自TABLESQ开始的16个单元连续存放着自然数0到15的平方值,任
  给一数X(0≤X≤15)在XX单元中,查表求出X的平方值,将结果存入YY单元中。
    首先在数据段中建立平方表,表的首地址为TABLESO,然后用程序找到X2值在平方表中的位置,即计算表地址,表地址 = 表起始地址(TABLESQ)+X,据此可写出如下程序。
    DATA    SEGMENT
     TABLESQ  DB O,1,4,9,16,25,36,49,
             DB 64,81,100,121,144,169,225
     XX   DB  ?
     YY   DB  ?
    DATA    ENDS
    STACK    SEGMENT PARA STACK‘STACK’
     DB  50 DUP(?)
    STACK    ENDS
    CODE    SEGMENT
    ASSUME  CS:CODE,DS:DATA,SS:STACK
    START    PROC FAR   *   *
    BEGIN:  PUSH DS    *   *
    MOV,AX,0          *   *
    PUSH AX             *   *
    MOV  AX,DATA
    MOV  DS,AX
    MOV  BX,OFFSET TABLESQ
    MOV  AH,0
    MOV  AL,XX
    ADD  BX,AX
    MOV  AL,[BX]
    MOV  YY,AL
    RET               *   *
    START  ENDP       *   *
    CODE   ENDS
END BEGIN
其中带 *   * 的六行语句的作用是为了在用户程序运行结束后,正确返回操作系统。    汇编语言源程序经过汇编、连接后生成 .EXE文件,可在DOS下直接键入文件名运行,例如命令 A>EXAMPLE 将把带有.EXE的文件EXAMPLE装入内存,并从程序中指定地址开始运行。装入文件并设置启动地址是由操作系统的COMMAND.COM文件完成的。COMMAND.COM文件在装入.EXE文件前,首先确定最低可用地址作为被装入程序的可用内存起点(该区段称为程序段)并在程序内从段内偏移地址处开辟100H字节的程序段前缀(PSP),在该PSP的前两字节存放一条 INT 20H 指令(即退出当前程序返回操作系统)。而COMMAND.COM把.EXE文件装入后,自动设置DS和ES寄存器指向程序段前缀,即此时的DS和ES值为程序段前缀之段基值。用户程序结束为RET,为使RET后程序转向程序段前缀中INT 20H指令,必须使CS为程序段前缀的段基值,IP为INT 20H的段内偏移值00H,因此必须把用户程序设计为过程,即加下START PROC FAR,在用户程序的前三条指令写入PUSH DS,MOV AX,0和PUSH AX,把DS = 程序段前缀之段基值,AX = 00H进栈,在用户程序最后用RET指令出栈到CS和IP,以保证程序转向INT 20H,进而返回操作系统。
    代码段结束的处理还可用DOS系统功能调用,在结尾处安排如下两条指令
    MOV AH、4CH
    INT 21H
    执行至此,返回调用程序,常用于应用DEBUG进行程序调试,可返回DEBUG。这 时,可不用把用户程序设计为过程,即去除带 *   * 的六行语句。
    例2  把非压缩十进制数转换为压缩十进制数,被转换数存放在DATl开始的两个单    元,转换后的数存回DATl单元。
    程序如下:
    MOV AX,DATI   ;(AX)=0109H
    MOV CL,4      ;(CL)=4
    SAL AH,CL     ;(AH)=10H
    ROL AX,CL     ;(AX)=0091H
    ROL AL,CL     ;(AL)=19H
MOV BYTE PTR DATl,AL    ;(DATl)=19H
    ┅
    DATl DW 0109H
    三、分支结构程序
    在实际的程序设计中,始终是顺序执行的情况是很少的,大部分程序在执行过程中,总是要求计算机作出一些判断,井根据判断作出不同的处理,这就引出了分支程序的概念。
    (一)分支程序的二要素
    分支结构程序是具有判断和转移功能的程序。
    1.判断——根据运算结果的状态标志
    判断前一定要经过运算(能影响状态标志的运算),状态标志反映了运算结果的特性。     这些状态标志是:进位标志CF、奇偶标志PF、零标志ZF、符号标志SF以及溢出标志OF。
    2.转移 — 主要由条件转移指令来实现(也可用无条件转移指令JMP)
    在8086/8088的指令系统中,条件转移指令可分为两大类,一类是按单标志位来判断  的,如JAE,JC,JZ,JO,JS等;另一类是按多标志位来判断,如JGE,JG,JA等,详见表3-6所示。
    分支结构程序框图如图4—1所示。
    (二)利用比较转移指令实现分支
    这是实现分支的一种常用方法,用于比较、判断的指令是CMP(比较指令)、CMPS(串比较指令)以及SCAS(串搜索指令)等。转移指令已如前述。而分支的次数可由具体问题决定是单重分支还是多重分支。n次判断可形成n+l路分支。
    例3  符号函数的处理
    有一符号函数
              1,  当 X>O  (-128≤X≤127)
       Y =     O,  当 X=0
             -1, 当 X<O
    设给定值x存放于LY单元,函数Y值存放于YY单元,则按X的不同取值给Y赋值的程序如下:
      MOV AL.XX
      CMP AL,0
      JGE BIGR
      MOV AL,0FFH
      MOV YY,AL    ;X<0时,—1送入YY单元
      HLT
   BIGR:JE EQUL
      MOV AL,1
一    MOV YY,AL    ;X>O时,l送人YY单元
        HLT
     EQUL:MOV  YY,AL    ;X=O时,O送人YY单元
        HLT
    这是一个多重分支的符号函数赋值程序,其流程图见图4-2 所示。
    例4数据块传送程序
    要求把内存中某一区域的源数据块传送到另一区域。在编写该程序时,必须判别一下    源数据区同目的数据区之间有无重叠,以决定是增量传送还是减量传送。程序清单如下:
    DATA    SEGMENT
    STRG    DB l000 DUP(?)
    STGl    EQU STRG+7
    STG2    EQU STRG+25
    SIRSE   EQU 50
    DATA    ENDS
    STACK   SEGMENT PARA STACK‘STACK’
    STARN    DB l000 DUP(?)
    STACK    ENDS
    COSEG    SEGMENT
    ASSUME CS:COSEG,DS:DATA,ES:DATA,SS:STACK
    GOO  PROC  FAR
    BEGIN: PUSH DS
            MOV  AX,0
            PUSH AX
            MOV  AX,DATA
            MOV  DS,AX
            MOV  ES,AX
            MOV  CX,STRSE
    MOV  SI,OFFSET STGl
            MOV  DI,OFFSET STG2 
            CLD                   ;增量方式
PUSHI SI
ADD  SI,STRSE-1
            CMP  SI,DI
            POP  SI
            JB  OK
            STD                  ;减量方式传送
            ADD  DI,STRSE-1     ;指向数据块底部
            ADD  SI,STRSE-1
        OK:REP MOVSB            ;重复传送50个数据
              RET
GOO    ENDP
        COSEG    ENDS
          END BEGIN
    (三)利用跳转表实现分支
    1.跳转表的建立
    在内存的一个连续区中,连续存放一系列跳转地址、跳转指令或关键字,组成一个决
  定程序分支的跳转表。
     例5  某工厂有8种产品的加工程序RO到R7分别存放在以SBRO,SBRl,…,SBR7为首地址的内存区域中,这8个首地址的偏移量连续存放在以BASE为首地址跳转表内,如图4-3 所示。
2.跳转表的使用
上例中,8种产品的编号为0,1,2,…,7 如果已知目前要加工的产品编号,要求编写程序,利用跳转表自动转入该产品的加工程序。
    在跳转表已知的情况下,关键问题要计算所要求的加工程序的入口地址在跳转表中的地址,即计算表地址。从图 4-3可见:
表地址=表基地址+偏移量
表基地址即跳转表的首地址,偏移量即对应的程序入口地址在表中的地址与表基地址
的距离。在本例中,分析表的结构可见,偏移量=产品编号 * 2,已知偏移量后,即可求出表地址。据此可写出[例5]的程序清单
    DATA    SEGMENT
    BASE  DW SBR0,SBRl,SBR2,SBR3
          DW SBR4,SBR5,SBR6,SBR7
    BN    DB ?
    DATA  ENDS
    STACK SEGMENT PARA STACK ‘STACK’
      DB  l00 DUP(?)
    STACK ENDS
    COSEG  SEGMENT
       ASSUME CS:COSEG,DS:DATA,SS:STACK
    START PROC FAR
    BEGIN:PUSH  DS
           MOV  AX,O
           PUSH  AX
           MOV  AX,DATA
           MOV  DS,AX
MOV  AL,BiN
                            MOV  AH.O
           ADD  AL,AL
           MOV  BX,OFFSETBASE
           ADD  BX,AX
           MOV  AX,[Bx]
           JMP  AX
           RET
           START  ENDP
           COSEG  ENDS
           END BEGIN 
    3.根据跳转表内指令分支
    在计算机系统的监控程序,键盘管理程序中经常要用到跳转表。在跳转表中既可存放    跳转地址(如例5),也可存放一系列的跳转指令JMP。
    例6  有一监控程序的键盘程序控制12个命令键——执行键(EXEC)、存储器检查键(MEM)等等,按下任一命令键相当于发出一条键盘命令,而这些命令的实现分别由监控程序中的12个于程序完成,这些子程序的入口地址分别为ADR0,ADRl,…,ADRll。据此可以  组成命令键跳转表如图4-4所示。该命令键跳转表存放在以BASE0为首地址的内存区域中,表内存放着12条转移指令:
    JMP  ADR0,JMP  ADRl,  …,JMP ADRll。
设12个命令键的编号分别为0 ~ 11。命令跳转表中每三个单元存放一条转移指令,如果命令键的编号X已送入寄存器AL,则实现转向相应的命令子程序的程序如下
    MOV  AH,0
    MOV  BL,AL
    ADD  AL,AL
ADD  AL,BL
ADD  AL, BL
    MOV  BX,OFFSET BASE0
    ADD  BX,AX          ;表地址计算
    JMP  BX
  
四、循环结构程序
    (一)概述
    1.循环程序
    实现重复执行某一段程序的程序结构称为循环程序。用来处理带重复性的问题,可以缩短源程序及目标程序。
    循环结构程序的框图之一如图4-5所示。
    循环程序由五部分组成:
    (1)初始化部分——这是循环的准备部分,为程序操作、地址指针、循环计数、结束条件等设置初始值。
   (2)循环工作部分——这是循环程序的主体,完成程序的基本操作,循环多少次,这部分的语句就执行多少次。
    (3)循环修改部分——修改循环工作部分的变量地址等,为下一轮重复操作作准备。
    (4)循环控制部分——修改计数器或判断循环结束条件以决定是继续循环还是终止循环,是典型的分支结构,不过继续
  循环的分支去向是循环体的头部。通常把循环工作部分称为循环体,循环体的第一条指令为循环体的头部。
    (5)循环结束部分——循环终止后,对循环结果的处理部分。
    2.循环程序的几种结构形式
    循环程序除图4-5所示的基本结构形式外,还有两种常用的结构形式,如图4-6和图
4-7所示。
    这两种结构的特点是工作部分放在循环控制部分之后,这样可以允许循环次数为零的    循环程序或不需要循环时不进入循环工作部分。
    3.循环程序分类
    (1)按循环控制条件分类
    1)计数循环——重复次数已知,用计数值控制循环的开始与终止。
    2)条件控制循环——重复次数未知或不确定,需找出循环控制的条件。
    (2)按循环体内结构分类
    1)单重循环——循环体内只是顺序程序或分支程序,不再有循环程序。
    2)多重循环——循环体内再套有循环程序。可把重复处理部分独立出来,前面加上    循环准备,后面加上结束判断。
    4.循环程序设计要点
    循环程序设计要点可归结为两点:
    (1)怎样把求解的问题变为循环结构的程序类型——怎样实现重复,即计算方案的循    环化。这里循环工作部分的设计尤为重要,因为这是多次重复的部分,注意程序的精练,及循环体头部的确定。
    (2)怎样使循环准确地执行完毕,这就要注意循环控制部分的设计。特别是对条件控    制的循环,注意设置循环结束标志。
    (二)循环程序举例
    例7计算Y=∑ai  , 0≤i≤100
有100个数a1,a2,…,a100,求这100个数之和(设和值不大于2个字节)。采用循环结构可写出如下程序:
DATA  SEGMENT
    TABL  DW a1,a2,…,a10
          DW all,a12,…,a20
           …      
          DW a91,a92,…,a100
    YY    DW ?
    DATA  ENDS
    STACK  SEGMENT PARA  STACK‘STACK’
          DB  l00 DUP(?)
    STACK  ENDS
    CODE  SEGMENT
      ASSUME CS:CODE,DS:DATA,SS:STACK
    GO  PROC FAR
    BEGIN:PUSH DS
           MOV AX,O
           PUSH AX
MOV AX,DATA
MOV DS, AX
MOV AX,0
MOV BX, OFFSET TABL  
MOV CX, 100
LOP:   ADD AX,[Bx]
INC BX
INC BX
DEC CX
JNZ LOP
MOV YY,  AX
RET
GO:    ENDP
CODE   ENDS
例8         统计数组中负元素的个数
数据段的定义如下:
DATA  SEGMENT
D1 DB -1,-3,5,6,-9,…
COUNT EQU $-D1
RS DW ?
DATA  ENDS
代码段程序为:
CODE SEGMENT
ASSUME CS:CODE,SS:STACK, DS:DATA
START PROC FAR
BEGING: PUSH DS
MOV AX,0
PUSH AX
MOV AX,DATA
MOV DS,AX
MOV BX,OFFSET D1     ;建立数据指针
MOV CX,COUNT         ;置计数器初值
MOV DX,0             ;置结果初值
LOP1: MOV AL,[BX]
CMP AL,0
JGE JUS
INC DX
JUS:  INC BX
DEC CX
JNZ LOP1
MOV RS,DX
RET
START ENDP
CODE ENDS
以上两例都是‘‘先执行,后判断”的结构。
 
    例9  寄存器AX中有一个16位二进制数,编程统计其中“1”的个数,结果存放在
CX中。
本例最好采用“先判断、后执行”的结构,即先检查凡x中有无为“1”的位。则代码段的有关程序如下:
MOV  CX, 0
LOP:  AND  AX, AX
JZ   STP
SAL  AX, 1
JNC  LOP
INC  CX
JMP  LOP
STP: HLT
例10  软件延时程序如下:
SOFTDLY PROC
MOV BL,10
DELAY: MOV CX, 2801
WAIT:  LOOP WAIT
DEC BL
JNZ DELAY
RET
 SOFTDLY ENDP
    这是一个二重循环结构,内循环即WAIT:LOOPWAIT,每个内循环可实现延时10ms左右,外循环人口为DELAY:MOV CX,2801,共进行10次,总的延时时间约lOOms。
    注意:可以从内循环直接跳到外循环,但不能从外循环直接跳进内循环。特别要注意  100循环体头部,防止死循环。
    五、子程序
    (一)概述
    1.子程序结构
    如果在一个程序中的多个地方或在多个程序中都要用到同一段程序,可以把该程序段    独立出来存放在内存的某一区域以供其他程序调用,这段程序称为子程序或过程。可见子    程序是可供其他程序调用的独立的、相对固定的程序段。调用子程序的程序体称为主程序    或调用程序。在实用中总把常用的子程序标准化后存放在一个内存区中,称为“子程序    库”。
    (1)结构。子程序的第一个语句前必须有“过程名”——入口地址的符号表示,出口是返回指令RET。
    (2)调用与返回。主程序通过书写调用指令CALL后跟子程序的入口地址来调用子    程序,8086/8088允许子程序在现行代码段中(子程序名为NEAR类型),也可以不在现行代码段中(子程序名为FAR类型)。为了保证正确返回,RET指令的类型,必须与CALL指令的类型相匹配。
    1)调用指令中的目标地址有两种表示方法:
    a.直接调用,目标地址就在指令中。例如:CALL NEAR-PRG  。
b.间接调用,目标地址在由指令指定的寄存器或内存单元中。例如:CALL DWORD PTR[BX]    对段内的直接调用而言,CALL指令首先将SP减2,使断点的IP栈,从指令中得到的目标过程的相对偏移量(最大不能越界)加到IP上。对段内的间接调用而言,以CALL AX为例,SP减2,IP进栈,AX→IP。
对段间的直接调用而言,SP减2,现行代码段寄存器CS的内容进栈,CS由指令中目标过程的段基值代入。SP再减2,IP进栈,然后将指令中的偏移地址代入IP。
    对段间的间接调用而言,SP减2,把现行的CS值进栈,CS由指令指定的双字存储器指针的第二个字的内容代入,SP再减2,IP进栈,然后IP由指令中指定的双字指针的第一个字的内容代入。双字存储器由数据段定义。
    2)返回指令RET也有两种情况:
    a. 段内返回指令,把SP所指的堆栈顶部的一个字的内容弹回IP,SP加2。
    b.段间返回指令,把SP所指的堆栈顶部的两个字的内容,先弹回IP,后弹回CS,SP加4。
    2.子程序文件
    子程序应以文件形式编写,子程序文件由子程序说明和子程序构成。
   (1)子程序说明。子程序说明包括如下部分:
    1)功能描述:包括子程序名称、功能,以及性能指标(如执行时间)等o
    2)所用寄存器和存储单元。
    3)子程序的入口、出口参数。
    4)子程序中又调用的其他子程序。
    5)调用实例(可无)。
 子程序说明举例如下:
    ;子程序PTOB
    ;两位十进制数(BCD码)转换成二进制数
    ;入口参数:AL中存放被转换数
    ;出口参数:CL中存放转换后的二进制数
    ;所用寄存器:BX
    ;执行时间:0.06ms
  (2)子程序。在8086/8088中,子程序本身常以过程形式存放在代码段中,以一个过    程名开始,以RET指令结束。
    例如: DTOB  PROC
           …
RET
           DTOB  ENDP
    3.子程序应用中应注意的问题
    (1)主程序与子程序的连接。主程序与子程序的连接由下例可见:
    例如;
    CODE  SEGMENT
      START:…  
          MOV AL,XX    ;被转换的数在XX单元
          PUSH BX
    CALL DTOB
    MOV YY,CL    ;转换结果存于YY单元
    POP BX
     …
    DTOB  PROC  
    RET
    DTOB    ENDP
    CODE    ENDS
    END START
    (2)子程序中所用寄存器及工作单元内容的保护。为了不破坏原有信息,对于在子程    序中要用到的某些寄存器和存储单元的内容,必须压入堆栈加以保护,也可存入一些空闲单元或某些目前不用的寄存器中。称为现场信息的保护。保护可以在子程序中实现,可在主程序中实现,一般在子程序中实现保护比较好。而对于用作中断服务的子程序,则一定在子程序中安排保护指令,因为中断的出现是随机的,无法在主程序中安排保护程序。
    (3)参数的传递。主程序与子程序通常需要交换信息,在多数情况下主程序需要向子  程序传递参数,子程序执行的结果要传送给主程序。子程序清单中的人口参数使子程序  对不同的数进行处理,出口参数可送出不同的结果。
     参数传递一般有三种方法:
  1)用寄存器传递,适用于参数较少的情况;
  2)用参数表传递,适用于参数较多的情况,要求事先建立参数表,参数表一般建立在内存中。
  3)用堆栈传递,适用于参数多并子程序有嵌套、递归调用的情况,主程序将参数压入堆栈,子程序中将参数从堆栈弹出。
4.子程序嵌套和子程序递归
    (1) 子程序嵌套 。子程序调用别的子程序,称为嵌套,。嵌套的层次只受空间的大小
的限制。
    (2)子程序递归。子程序直接或间接地调用子程序自身,称为递归。
    (二)子程序结构举例
    例11  数据段定义了两个数组ARYl和,ARY2,编一程序分别求此两数之和。主程序和过程分别安排在两个不同的段中,因此过程应是FAR类型。
    该程序清单如下
    STACK  SEGMENT PARA STACK
     DW 20 DUP(?)
STACK  ENDS
DATA  SEGMENT
ARY1 DB 100 DUP(?)
SUM1 DW ?
ARY2 DB 50 DUP(?)
SUM2 DW ?
DATA  ENDS
MAIN  SEGMENT          ;主程序段       
   ASSUME CS:MAIN, DS:DATA,SS:STACK
STR:PUSH DS
MOV AX,0
PUSH AX
MOV AX,DATA
MOV DS,AX
MOV AX,SIZE ARY1
PUSH AX           ;SUM过程的入口参数1 进栈
MOV AX,OFFSET ARY1
PUSH AX          
CALL SUM
   …
MOV AX,SIZE ARY2
PUSH AX           ;SUM过程的入口参数2 进栈
MOV AX,OFFSET ARY2
PUSH AX          
CALL SUM
HIT
MAIN  ENDS
 
 
 PROCE  SEGMENT     ; 过程段
        ASSUME  CS:PROCE,DS:DATA,SS:STACK
SUM  PROC FAR
PUSH AX
PUSH BX
PUSH CX
PUSH BP
N40V BP,SP
PUSHF
MOV CX,[BP+14]
MOV BX,[BP+12]
MOV AX,0
AND:  ADD AL,[BX]
INC BX
ADC AH,0
LOOP ADN
MOV[BX],AX     ;数组之和送到结果区
POPF            ;恢复现场
POP BP
POP CX
POP BX
POP AX
RET 4            ;返回并废除参数1和2
SUM  ENDP
PROCE  ENDS
END STR
 
从程序清单可见主程序向过程的参数传递是通过堆栈实现的,程序执行过程中堆栈变化如图所示。
    图4-8  例u中堆栈变化情况
 
程序中出现RET 4,该指令的功能是返回后,SP再加 4
例12  求n!
算法是
n! =  n(n-1)!   当n>0时;
  1         当n=0时
设数n存放在AL中,n! 存放在BX中。
;主程序
MAIN: MOV AX,3    ;设n=3
       CALL FACT
  X1: MOV BX,DX
       HLT
;阶乘子程序
;人口参数:AL中存放n
;出口参数:DX中存放n!
;所用寄存器:CX
FACT  PROC
CMP AL,0
JNZ IIA
MOV DL,1
RET               ;①
IIA: PUSH AX
DEC  AL
CALL FACT
X2:  POP CX
CALL MULT
X3:  MOV DX,AX
RET           ;②
FACT  ENDP
;无符号字节数乘法子程序
;入口参数:CL、DL中各为一乘数
;出口参数:AX中为乘积
MULT  PROC
… 
 RET     ;③
  MULT  ENDP
    该程序既包括子程序嵌套,也包括子程序递归。该子程序名为FACT,嵌套子程序即
FACT调用另一子程序MULT(无符号字节数乘法子程序),递归子程序即FACT调用FACT。注意三个RET指令的作用。

本文标签:江西自考 工学类 微型计算机原理及应用学习笔记 汇编语言程序设

转载请注明:文章转载自(http://www.zikao.jx.cn

本文地址:http://www.zikao.jx.cn/cj_gxl/8716.html


《江西自考网》免责声明:

1、由于各方面情况的调整与变化,本网提供的考试信息仅供参考,考试信息以省考试院及院校官方发布的信息为准。

2、本网信息来源为其他媒体的稿件转载,免费转载出于非商业性学习目的,版权归原作者所有,如有内容与版权问题等请与本站联系。联系邮箱:812379481@qq.com。