第三章:汇编语言程序设计(二)

汇编语言程序格式

汇编语言数据的定义 (数据定义伪指令)

在程序中用到的所有数据必须指定其类型,数据分为常量和变量

常量

  1. 汇编源程序中具有固定值的数称为常量;
  2. 常量可以以多种数制及字符形式出现
  3. 汇编程序中常使用一下两种类型的常量
    1. 数字常量
      1. 二进制数:10100001B
      2. 八进制:76Q,335O
      3. 十进制: 98,4330
      4. 十六进制: 0FE65H
    2. 字符串常量
      1. 字符串常量表示为包含在两个单引号之间的字符,如:’AB‘,‘123’
      2. 程序经编译后,单引号内的每个字符实际上是其ASCII码的形式,例如:'AB',可以看成是4142H.

变量的定义

  1. 变量是用户为存放数据的内存单元所起的名字
  2. 变量名实际上是内存单元的符号地址
  3. 通常这些内存单元被分配在数据段、附加段或堆栈段,这些单元的数据在程序的运行期间可以随时修改。
  4. 在汇编程序中,用数据定义伪指令为变量分配内存单元,同时可以将这些内存单元预置初值。
    1. 字节定义语句 DB
      1. 定义字节存储单元,其后的每个操作数占有一个字节单元,可以连续存放。
      2. 格式:[变量名] DB 操作数 [,....操作数]
      3. 举例:

         //单字节数据
         X DB 10   ;为变量X分配一个字节,初值是10
         Y DB ?    ;为变量Y分配一个字节,不设置初值,初值是随机值
                    
         //多字节数据
         BUFFER DB 3,4,-5,-2    ;从地址BUFFER开始,连续分配4个字节,其内容分别是03H,04H,0FBH,0FEH(负数是以补码的形式在计算机中存在的)
                    
         //定义字符串
                    
         STRING DB 'ABCD'     ;从地址STRING开始,连续分配4个字节,其内容分别是41H,42H,43H,44H
         //定义重复数据
         BUF DB 100 DUP(0)    ;从地址BUF开始,连续分配100个字节,并将其全部赋值为0.
         复制操作符DUP用来复制某个或某些连续的存储空间,并且可以嵌套使用。
        
    2. 自定义语句 DW
      1. 定义字存储单元,其后的每个操作数占据两个字节,地位字节在低地址,高位字节在高地址,可以连续存放。
      2. 自定义单元的定义格式与操作数的形式与字节定义语句DB类似。
      3. 注意: 如果DW后的操作数是地址(变量名),则存入对应字单元的是地址(变量名)的偏移地址部分。

         DAT SEGMENT
           X DB 3,4
           Y DW -2
           ADDR DW Y
         DAT ENDS
         其中,ADDR字单元内存储的事变量Y的偏移地址
        
    3. 其他数据定义语句DD/DQ/DT等
      1. DD定义双字存储单元,其后的每个操作数占据4个字节
      2. DQ定义的每个数据单元占8个字节
      3. DT定义的每个数据单元占10个字节

变量的属性

  1. 由于存储器是分段使用的,所以对源程序中定义的变量都有以下3种属性。
    1. 段属性: 变量所在段的段基址,为该段所对应的段寄存器的值。
    2. 偏移属性:变量距段起始地址的字节数,为16位二进制无符号数。
    3. 类型属性: 变量在存储单元所占的字节长度,为整数数值,主要有BYTE(1),WORD(2),DWORD(4),DQ(8),DT(10)等。

汇编语言的标号

  1. 标号的概念
    1. 标号是用户为程序中某条指令所起的名字,实质上是代码段中该指令所对应的目标代码在存储器内的存放地址。
    2. 标号经常在转移、调用指令中引用,作为程序转移的目标地址。
  2. 标号的属性
    1. 段属性: 标号所在段的段基址,为该段所对应的段寄存器的值,即CS的值。
    2. 偏移属性: 标号所代表的指令相对于代码段首地址的字节数,为16位二进制无符号数
    3. 类型属性: 若标号所代表的指令与引用标号的指令在同一代码段内,则为NEAR类型,用0FFFFH(1)表示;否则为FAR类型,用0FFFEH(-2)表示。
    4. 可见:变量代表的事数据存储单元的地址,而标号代表的是指令存储单元的地址。

汇编语言的运算符和表达式

  1. 汇编语言的数据或者操作数除了表示为常量、变量和标号的形式外,还可以表示成表达式的形式。
  2. 注意: 表达式并不是命令,其计算或操作都是在汇编时由汇编程序完成的,在程序运行时表达式已经被他的运算结果取代了
  3. 表达式根据其运算结果的类型分为数值表达式和地址表达式。
    1. 数值表达式的运算结果是数值,没有别的属性;
    2. 地址表达式的运算结果是地址,具有段属性、偏移属性和类型属性
  4. 构成表达式的运算符有5种,分别是算术运算符、逻辑运算符、关系运算符、分析运算符合合成运算符。
    1. 所有运算符如课本表:4.1 p119
    2. 这积累运算符的优先级: 4.2 p119

算术运算符

  1. 算术运算符共包括:加(+)、减(-)、乘(*)、除(/)、取余(MOD)、左移(SHL)、右移(SHR)共7种
  2. 说明:
    1. 参加运算的数值和运算结果均是整数
    2. “/”运算的结果取两个运算数商的整数部分,“MOD”是取两个运算数商的余数部分;
    3. SHL/SHR表示的是在汇编阶段对一个具体的整数值进行左移、右移运算
    4. 注意:左移算术运算符SHL与逻辑左移指令SHL是完全不同的两种操作,左移运算符是在汇编程序时完成的操作; 逻辑左移指令是在程序运行时进行的操作,如下指令:

       SHL BL, 1       ; 执行到该指令时,寄存器BL的内容左移一位。
      
  3. 算术运算符都可以用于数值表达式,其中的“+”、“-” 还可以用于地址表达式,有以下3种形式
    1. 地址表达式 + 常数
      1. 运算结果为偏移地址,与地址表达式的类型和段基址相同,但偏移地址是地址表达式的偏移地址加上常数个字节。
    2. 地址表达式 - 常数
      1. 运算结果为偏移地址,与地址表达式的类型和段基址相同,但偏移地址是地址表达式的偏移地址减去常数个字节。
    3. 地址表达式1 - 地址表达式2
      1. 结果是一个数值,表示两偏移地址间距的字节数。 需要注意的是,地址表达式1和地址表达式2必须在同一个逻辑段中,即这两个地址必须有同一个段基址。

逻辑运算符

  1. 逻辑运算符包括与(AND)、或(OR)、非(NOT)、异或(XOR)4种
  2. 逻辑运算符所处理的操作数均是数值型整数,在两个整数之间按位进行二进制运算,结果也是整数。

关系运算符

  1. 关系运算符包括等于(EQ)、不等于(NE)、小于(LT)、小于等于(LE)、大于(GT)大于等于(GE)6种
  2. 关系运算符用于比较两个表达式,这两个表达式可以是常数表达式或同一段内的地址(变量)表达式。
  3. 若为常数表达式,按照无符号数的规则进行比较,若为你同一段内的地址(变量)表达式,则比较他们的偏移地址。
  4. 关系运算符的结果有两种,若关系成立,结果为逻辑真值,用0FFFH表示;否则,为逻辑假值,用0表示。

分析运算符

分析运算符用来分析一个操作数(变量或标号)的属性,即将其不同的属性(段地址、偏移地址、类型、字节总数、数据项总数)用数值表示出来

  1. SEG 运算符
    1. 格式 : SEG 变量或标号
    2. 功能: 返回变量或标号所在段的段基址,为16位二进制数。
  2. OFFSET 运算符
    1. 格式: OFFSET 变量或标号
    2. 功能: 返回变量或标号的段内偏移地址,为16位二进制数
  3. TYPE 运算符
    1. 格式: TYPE 变量或标号
    2. 功能: 返回变量或标号的类型值
    3. 对变量而言,类型返回值分别为: 1(DB),2(DW),4(DD),8(DQ),10(DT)等,
    4. 对标号而言,类型返回值分别为: -1(NEAR)和 -2(FAR)
  4. LENGTH 运算符
    1. 格式: LENGTH 变量
    2. 返回所占内存储单元内变量的个数
    3. LENGTH 操作符只与定义变量的伪指令后的第一个参数有关,若第一个参数的形式为“n DUP(表达式)”,则LENGTH运算的结果为n,否则为1
  5. SIZE 运算符
    1. 格式 SIZE 变量
    2. 功能: 返回所占存储单元内变量的总字节数,即: SIZE = LENGTH * TYPE
  6. HIGH 和 LOW 运算符
    1. 格式: HIGH 表达式 LOW 表达式
    2. 功能: 分离运算符,取字操作数的高位字节和低位字节
    3. 注意: 表达式必须具有常量值,如常数,地址表达式的偏移量等。HIGH 和 LOW 运算符不能对存储器或寄存器操作数进行分离。

合成运算符

  1. PTR 运算符
    1. 格式:新类型 PTR 变量 或 标号
    2. 功能: 将原有变量或标号的类型改变成新类型,这种改变是临时的,仅在有此操作符的语句内有效。
    3. 举例:

       DAT DD 12345678H    ; 定义DAT 为双字类型的变量
       MOV AX WORD PTR DAT ; 从DAT中去一个字放置AX,(AX) = 5678H
      
  2. THIS 运算符
    1. 格式: 变量或标号 EQU THIS 类型
    2. 功能: 定义变量或标号的类型为指定的类型。
    3. 如果指定的事变量,该变量的段地址和偏移地址与下一个定义的存储单元相同;
    4. 如果指定的事标号,则标号表示的是下一条指令的地址。
    5. 举例:

       NEXT1 EQU THIS FAR      ; 标号 NEXT1 被定义成FAR 类型,地址与NEXT相同
       NEXT: XOR AX, AX        ; 标号NEXT默认为NEAR 类型
       ···
       JMP  NEXT               ; 段内转移
       ···
       JMP NEXT1               ; 段间转移
      
  3. SHORT 运算符
    1. 格式: SHORT 标号
    2. 功能: 说明其后的标号在距当前指令一个字节的范围内(-128 ~ 127),常用于转移指令,限制转移的目标地址的范围。

汇编语言的其他常用伪指令

  1. 符号定义语句
    1. 如果汇编程序多次使用某个表达式、常数或指令,就可以把它定义成一个易于记忆的符号
    2. 凡是在后面程序中用到这些表达式、常数或指令的地方都用这个符号来代替。
    3. 这样既便于修改,有提高了程序的可读性,汇编后这个符号是代表确定的内容。
    4. 这样的符号定义语句有以下2种:
      1. 等值伪指令 EQU
        1. 格式: 符号 EQU 表达式
        2. 功能: 将表达式或表达式的值赋值给符号,在后续程序中用符号代替表达式或表达式的值。 表达式可以是常数、数值表达式、地址表达式、变量名、标号、寄存器或指令助记符等。
        3. 举例:

            COUNT EQU 10  ;
            CALUE EQU 78+89   ;  
            ADR EQU [BX][SI]  ; 符号ADR 代表地址表达式 [BX][SI]
            NUM EQU VAL       ; 为变量VAL 另定义一个别名NUM
            LOD EQU MOV       ; 用LOD代表指令助记符 MOV
          
        4. 注意:在同一个程序中不能用EQU对同一个符号名重名定义,如:

           NUM EQU 10  ; 
           ···
           ···
           NUM EQU 20  ; 错误,不同在同一程序中对NUM 重复定义
          
      2. 等号伪指令
        1. 格式: 符号 = 表达式
        2. 功能: 与EQU伪指令相同,但是等号伪指令可以多次重复定义同一符号(覆盖旧值)。
  2. 类型定义语句 LABEL
    1. 格式: 名字 LABEL 类型
    2. 功能: 与THIS运算符类似,定义变量或标号的类型为指定的类型。 如果指定的事变量,改变量的段地址和偏移地址与下一个定义的存储单元相同;如果指定的事标号,则标号表示下一条指令的地址。
  3. 定位伪指令 ORG
    1. 格式: ORG 常数表达式
    2. 功能: 指定下一个可用内存单元的偏移地址为常数表达式。 可用于数据段或代码段。
    3. 举例:

       DATA SEGMENT
          ORG 0100H
       BUF DB 20 DUP('A')
       DATA ENDS
       // 由于ORG伪指令,因此变量BUF的偏移地址为0100H。 如果没有ORG,则数据段的数据从偏移地址0000H开始一次存放
      
  4. 地址计数器 $
    1. 汇编程序在对源程序汇编时,为每个逻辑段设置一个地址计数器,用来记录正在汇编的数据或指令的目标代码在当前段的偏移量。
    2. 在程序中,地址计数器的值用 “$”表示。
    3. 即地址计数器“$”表示下一个可用单元的偏移地址。

宏指令

宏与子程序的区别这里就不在赘述

  1. 不带参数的宏定义语句
    1. 格式:

       宏名 MACRO
           ···
           ENDM
      
  2. 带参数的宏定义语句
    1. 格式:

       宏名 MACRO 参数1[,参数2,...]
           ···
           ENDM
      
  3. 取消宏名伪指令 PURGE
    1. 格式:

       PURGE 宏名1 [,宏名2,...]
      
  4. 宏标号定义语句 LOCAL
    1. 宏定义中如果包含语句标号,且在同一源程序中被多次调用,则在宏展开时,就要产生多个相同的语句标号。
    2. 而语句标号代表了改语句所在的内存地址,是不能重复的,这样就会使得程序出错。
    3. 为了避免这类错误,可以在宏定义中使用LOCAL伪指令,格式如下:

       LOCAL 标号1 [,标号2,...]
      
    4. LOCAL 伪指令仅在宏定义中使用,且必须是宏体中的第一条语句。
    5. 实际上LOCAL伪指令是将标号变为形式参数,在宏展开时用实际参数“??0000,??0001,??0002…”等进行替换,这样就避免了标号的重复定义
    6. 举例:

       SHIFTDW MACRO X, Y, n
             LOCAL L1         ;说明标号L1
             MOV CX, n
        L1 : SAL X, 1         ;前面声明了,在同一段程序中调用多次这个宏,L1标号都不会报错了。
             RCL Y, 1
             DEC CX
             JNZ LE
       ENDM
      

DOS 系统功能调用

  1. 什么是BIOS?
    1. 基本输入输出系统的英文缩写。
    2. 驻留在我们计算机ROM中的一段软件
    3. 计算机出厂的时候,整个设备唯一带的一个软件。
    4. 与芯片完全成为一体的,因此也成为固件。
    5. 早期存储系统BIOS的芯片完全是用光技术刻写在硅片上的一个芯片,即里面的BIOS系统是完全通过硬件写在里面的,不可以改动的,所以叫固件。
    6. 现在都是存储在可以修改的存储器中了。
    7. 即BIOS就是驻留在ROM中的基本输入、输出系统
  2. BIOS 的作用:
    1. 加电自检,装入引导,主要I/O设备处理程序即接口控制
      1. 一开机,系统出现黑乎乎的字符界面,不停地出现,这个就是上电自检
      2. 操作系统引导:引导操作系统从硬盘引导到内存中
      3. 对输入输出接口进行控制。
  3. DOS 是什么?
    1. 磁盘操作系统
    2. windows操作系统之前的操作系统,即今天操作系统的前身。
    3. 在Windows开发出来之后,DOS系统被嵌入到Windows中
    4. 因此在Windows开启后在程序-附件中可以看到命令提示符(cmd.exe,就是Windows的终端),这个就是DOS系统。
  4. DOS功能、BIOS功能调用是调用系统的内核子程序
  5. DOS功能、BIOS功能均通过中断方式调用
  6. DOS软中断
    1. DOS中断包括:设备管理、目录管理、文件管理、其他
    2. 这些所有的功能都是一个一个的软件。
    3. 这些软件的调用都是通过中断来调用的
    4. 用中断类型码来区分这些一个个软件
    5. DOS中软件功能包非常多,这里只介绍一个类型码(功能包)21H,也叫DOS软中断
    6. 下面介绍类型码为21H的这个功能包。
      1. 这个功能包中包含了很多子程序
      2. 这些子程序都有同一个类型码21H
      3. 每个子程序用功能号来区分
      4. 这个软中断中包含了几十个子程序,这里只介绍5个。
      5. 这5个子程序每个子程序都有一个功能号。
  7. DOS 功能调用的基本步骤
    1. 将调用参数装入指定的寄存器
    2. 将功能号装入AH
    3. 按中断类型号调用DOS中断
    4. 检查返回参数是否正确。
    5. 注意:由于DOS系统以及他的子程序都没有开源,所以我们只能通过入口参数、出口参数来调用。
  8. 调用格式:

     MOV AH, 功能号
     <置响应参数>
     INT 21H
    

常用的DOS 系统功能调用

  1. DOS的2号功能调用 - 显示单个字符
    1. 功能: 在屏幕的光标处显示单个字符
    2. 入口参数: 要显示字符的ASCII码放在DL中。
    3. 出口参数: 无
    4. 举例:

       MOV DL, 'AL'  ; 将待显示字符的ASCII码放在寄存器DL中
       MOV AH, 2     ; 设置功能调用号
       INT 21H       ; 执行2号功能调用,运行后,在屏幕的当前光标出显示字符‘A’
      
  2. DOS的9号功能调用 - 在屏幕上显示字符串
    1. 功能: 在屏幕上当前光标处输出存储在内存数据段的一串字符串,该字符串以‘$’结束。
    2. 入口参数: DS : DX指向欲显示字符串的首地址
    3. 出口参数: 无
    4. 举例:

       DATA SEGMENT
          STARING DB 'I am a student. $'
       DATA ENDS
       ...
       MOV DX, OFFSET STRING   ;入口参数DX指向字符串首地址
       MOV AH, 9               ;设置功能调用号
       INT 21H                 ; 执行9号功能调用
              
       执行后,在屏幕的当前光标处显示字符串 “I am a sutdent.”
      
  3. DOS的1号功能调用- 接收键盘输入的一个字符并回显
    1. 功能: 等待键盘输入,直到按下一个键。
    2. 入口参数:无
    3. 出口参数: 从键盘输入的字符的ASCII码放在AL中,并在屏幕上显示该字符。
    4. 举例:

       MOV AH, 01H   ;设置功能调用号
       INT 21H       ; 程序暂停运行,等待用户从键盘输入一个字符串后继续向下运行
       MOV [SI], AL  ; AL 存放着输入字符的ASCII码,同时输入的字符在屏幕上显示
      
  4. DOS的7号功能调用- 接收键盘输入的一个字符不回显
    1. 功能: 等待键盘输入,直到按下一个键。
    2. 入口参数:无
    3. 出口参数: 从键盘输入的字符的ASCII码放在AL中,但在屏幕上没有显示该字符,常用于输入密码
    4. 举例:

       MOV AH, 07H   ;设置功能调用号
       INT 21H       ; 程序暂停运行,等待用户从键盘输入一个字符串后继续向下运行
       MOV [SI], AL  ; AL 存放着输入字符的ASCII码,屏幕上并没有显示
      
  5. DOS的10号功能调用- 接收键盘输入字符串
    1. 功能: 将从键盘输入的以回车结束的一串字符存放到数据段的指定存储区。
    2. 入口参数:DS:DX 指向接收字符串的存储区的首地址,该地址的第一个字节是由用户设置的可输入字符串的最大字符数(含回车)
    3. 出口参数:将实际输入的字符数(不含回车)存放到存储区的第二个字节处,实际输入的字符串从该存储区的第三个字节处开始存放。
    4. 举例:

       DATA SEGMENT 
          BUF DB 10         ; 最大输入字符数
              DB ?          ; 保留一个字节返回实际输入的字符数
              DB 10 DUP(?)  ; 预留最大字符数的空间
       DATA ENDS
       ...
       LEA DX, BUF          ; 设置入口参数
       MOV AH, 0AH          ;设置功能调用号
       INT 21H              ; 程序暂停运行,等待用户从发键盘输入一串字符并按回车建后继续向下运行
      
      
  6. DOS的4CH号功能调用 – 结束用户程序返回DOS
    1. 功能: 将计算机控制权移交给DOS
    2. 入口参数: 无
    3. 出口参数: 无
    4. 举例:

       MOV AH, 4CH
       INT 21H
              
       该功能用于用户程序的非过程返回。
      
Table of Contents